Compare commits

...

No commits in common. "v1.3.2-tarball" and "main" have entirely different histories.

68 changed files with 13302 additions and 9381 deletions

1
.coveralls.yml Normal file
View file

@ -0,0 +1 @@
repo_token: xl1m2EiKjG4YlJQ0KjTTBNDRcAFD0lCVt

6
.dir-locals.el Normal file
View file

@ -0,0 +1,6 @@
((cperl-mode . ((dumb-jump-force-searcher . rg)
(cperl-indent-level . 4)
(cperl-close-paren-offset . -4)
(cperl-indent-subs-specially . nil)
(indent-tabs-mode . nil)
(eval . (auto-fill-mode -1)))))

2
.dumbjump Normal file
View file

@ -0,0 +1,2 @@
+bin/*.in
+lib/*.pm.in

80
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,80 @@
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
name: Test suite
on:
push:
branches: [master]
pull_request:
branches: [master]
types: [opened, synchronize, reopened, ready_for_review]
jobs:
# call-simple-perl-test:
# uses: perl-actions/github-workflows/.github/workflows/simple-perltester-workflow.yml@main
# with:
# since-perl: 5.14
test:
name: Perl ${{ matrix.perl-version }}
runs-on: ubuntu-latest
strategy:
matrix:
perl-version:
- '5.38'
- '5.36'
- '5.34'
- '5.32'
- '5.30'
container:
# This Docker image should avoid the need to run:
#
# cpanm -n Devel::Cover::Report::Coveralls
image: perldocker/perl-tester:${{ matrix.perl-version }}
steps:
- run: apt-get update && apt-get install -y sudo texinfo texlive
- name: Checkout code
uses: actions/checkout@v2
# - uses: awalsh128/cache-apt-pkgs-action@latest
# with:
# debug: true
# packages: texinfo texlive
# version: 1.0
- run: autoreconf --install
- name: ./configure && make
run: |
eval `perl -V:siteprefix`
# Note: this will complain Test::Output isn't yet installed:
./configure --prefix=$siteprefix && make
# but that's OK because we install it here:
make cpanm
#- name: Run tests
# run: make test
- run: make distcheck
- run: perl Build.PL
- run: ./Build build
- run: cover -test -report coveralls
- run: ./Build distcheck

35
.gitignore vendored Normal file
View file

@ -0,0 +1,35 @@
.dirstamp
/Build
/ChangeLog
/MYMETA.json
/MYMETA.yml
/Makefile
/Makefile.in
/bin/chkstow
/bin/stow
/doc/stow.info
/doc/version.texi
/playground/
tmp-testing-trees*/
_build/
autom4te.cache/
blib/
config.log
config.status
configure
/cover_db/
/doc/ChangeLog.OLD
/doc/manual.pdf
/doc/manual-single.html
/doc/manual-single-old-texi2html.html
/doc/manual-single-texi2html-wrapper.html
/doc/manual-split/
/doc/manual.texi
/doc/stow.pdf
/doc/stow.8
/lib/Stow.pm
/lib/Stow/Util.pm
stamp-vti
stow-[0-9].[0-9].[0-9].tar.*
stow-[0-9].[0-9].[0-9]/
Stow-v[0-9].[0-9].[0-9].tar.*

25
.travis.yml Normal file
View file

@ -0,0 +1,25 @@
language: perl
perl:
- "5.20"
- "5.18"
- "5.16"
- "5.14"
sudo: false
addons:
apt:
packages:
- texinfo
- texlive
before_install:
- cpanm -n Devel::Cover::Report::Coveralls
install:
- autoreconf --install
- eval `perl -V:siteprefix`
# Note: this will complain Test::Output isn't yet installed:
- ./configure --prefix=$siteprefix && make
# but that's OK because we install it here:
- make cpanm
script:
- make distcheck
- perl Build.PL && ./Build build && cover -test -report coveralls
- ./Build distcheck

80
AUTHORS
View file

@ -1,7 +1,79 @@
Stow was written by Bob Glickstein <bobg+stow@zanshin.com>, Zanshin
Software, Inc.
This file documents the high-level history of Stow, and some of its
major contributors. See also the THANKS file for a more complete list
of contributors.
Contributions from Gord Matzigkeit <gord@enci.ucalgary.ca>.
Stow was originally written by Bob Glickstein <bobg+stow@zanshin.com>,
Zanshin Software, Inc.
Gord Matzigkeit <gord@enci.ucalgary.ca> made some early contributions.
John Bazik wrote `fastcwd', the Perl subroutine for computing the
current working directory.
current working directory (later removed in 1.3.3).
Charles Briscoe-Smith <cpbs@debian.org> wrote the fix to prevent
stow -D / stow -R removing initially-empty directories (mentioned
in 1.3.3 section of NEWS).
Adam Lackorzynski <al10@inf.tu-dresden.de> wrote the fix to prevent
the generation of wrong links if there are links in the stow directory.
Stow was maintained by Guillaume Morin <gmorin@gnu.org> up to November
2007. Guillaume originally imported the source code into the Savannah
CVS repository on 2001/12/24 with the tag "v1_3_2". This history was
later imported into git as described below.
1.3.3 was the last release of the 1.x series. The CVS history
contains a few commits after 1.3.3 preparing for a 1.3.4 release which
was never published (see the "import-cvs" tag in git).
Between 2007 and 2009, a small team of people collaborated on a
private in-house project on Stow:
https://lists.gnu.org/archive/html/stow-devel/2011-11/msg00003.html
Kahlil (Kal) Hodgson <kahlil@internode.on.net> performed a major
rewrite in order to implement:
1. deferred operations,
2. option parsing via Getopt::Long,
3. options to support shared files,
4. support for multiple operations per invocation,
5. default command line arguments via '.stowrc' and '~/.stowrc' files,
6. better cooperation between multiple stow directories,
7. a test suite (and support code) to ensure that everything still works.
As these changes required a dramatic reorganisation of the code, very
little was left untouched, and so Stow's major version number was
bumped up to 2. Austin Wood <austin.wood@rmit.edu.au> and Chris
Hoobin <christopher.hoobin@rmit.edu.au> helped clean up the
documentation for the new 2.x.y series, and created the texi2man
script.
Kahlil obtained permission to donate these changes back to GNU. The
Subversion history from this period is no longer accessible, so the
breakdown of the individual changes to the source code between 1.3.3
and the unreleased 2.0.2 version have been lost; however some details
are still visible in ChangeLog.OLD, which also acknowledges the
contributions of Geoffrey Giesemann and Emil Mikulc.
Sometime after this, Troy Will took over maintainership and imported
the unreleased 2.0.2 code base as the original root commit into
Savannah git repository.
On 25th November 2011, Adam Spiers <stow@adamspiers.org> took over
maintainership. He imported the CVS history into the Savannah git
repository, grafting it onto the previous root commit imported by
Troy, and tagged this as v2.0.2:
https://lists.gnu.org/archive/html/stow-devel/2011-11/msg00001.html
https://lists.gnu.org/archive/html/stow-devel/2011-11/msg00002.html
refactored the backend code into new Stow.pm and Stow/Util.pm modules
providing an OO interface, tightened up the test suite, added support
for ignore lists, `make test', and distribution via CPAN, and cleaned
up numerous other minor issues.
These changes were included in 2.1.0, which was the first official
release since 1.3.3 in 2002.
Stow is currently maintained by Adam Spiers.

102
Build.PL Normal file
View file

@ -0,0 +1,102 @@
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
use strict;
use warnings;
use Module::Build;
# These are required by the test suite.
use lib "t";
use lib "bin";
my $build = Module::Build->new(
module_name => 'Stow',
keywords => [ qw/stow symlink software package management install/ ],
license => 'gpl',
# Module::Build forces us to use v1.4 of the CPAN Meta Spec:
# https://rt.cpan.org/Ticket/Display.html?id=71502
# 'meta-spec' => {
# version => '2.0',
# url => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
# },
meta_add => {
resources => {
license => 'http://www.gnu.org/licenses/gpl-2.0.html' ,
homepage => 'https://savannah.gnu.org/projects/stow',
# Module::Build forces us to use v1.4 of the CPAN Meta Spec:
# https://rt.cpan.org/Ticket/Display.html?id=71502
# bugtracker => {
# web => 'http://rt.cpan.org/Public/Dist/Display.html?Name=Stow',
# mailto => 'stow-devel@gnu.org',
# },
#bugtracker => 'http://rt.cpan.org/Public/Dist/Display.html?Name=Stow',
# Module::Build forces us to use v1.4 of the CPAN Meta Spec:
# https://rt.cpan.org/Ticket/Display.html?id=71502
# repository => {
# url => 'git://git.savannah.gnu.org/stow.git',
# web => 'https://savannah.gnu.org/git/?group=stow',
# type => 'git',
# },
repository => 'git://git.savannah.gnu.org/stow.git',
},
},
requires => {
'perl' => '5.006',
'Carp' => 0,
'IO::File' => 0,
},
script_files => [ 'bin/stow', 'bin/chkstow' ],
all_from => 'lib/Stow.pm.in',
configure_requires => {
'Module::Build' => 0,
},
build_requires => {
'Test::More' => 0,
'Test::Output' => 0,
'IO::Scalar' => 0,
},
);
if (system('grep', '-q', '^use lib ', 'bin/stow') >> 8 == 0) {
die <<'EOF';
ERROR: bin/stow contains 'use lib' line which could interfere
with CPAN-style installation via Module::Build. To avoid this,
you should run ./configure with parameters which result in
--with-pmdir's value being in Perl's built-in @INC, and then run
'make' (NOT 'make install') to regenerate bin/stow, e.g.
eval `perl -V:siteprefix`
./configure --prefix=$siteprefix && make
or
./configure --with-pmdir=`PERL5LIB= perl -le 'print $INC[0]'` && make
Then re-run this script.
Note that these parameters are chosen purely to regenerate
bin/stow without a 'use lib' line, so don't run 'make install'
while Stow is configured in this way unless you really want an
installation using these parameters.
EOF
}
$build->create_build_script();

123
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,123 @@
Contributing to GNU Stow
========================
Development of Stow, and GNU in general, is a volunteer effort, and
you can contribute. If you'd like to get involved, it's a good idea to join
the [stow-devel](https://lists.gnu.org/mailman/listinfo/stow-devel)
mailing list.
Bug reporting
-------------
Please follow the procedure described in [the "Reporting Bugs"
section](https://www.gnu.org/software/stow/manual/html_node/Reporting-Bugs.html#Reporting-Bugs)
of [the manual](README.md#documentation).
Development
-----------
For [development sources](https://savannah.gnu.org/git/?group=stow)
and other information, please see the [Stow project
page](http://savannah.gnu.org/projects/stow/) at
[savannah.gnu.org](http://savannah.gnu.org).
There is also a
[stow-devel](https://lists.gnu.org/mailman/listinfo/stow-devel)
mailing list (see [Mailing lists](README.md#mailing-lists)).
Please be aware that all program source files (excluding the test
suite) end in `.in`, and are pre-processed by `Makefile` into
corresponding files with that prefix stripped before execution. So if
you want to test any modifications to the source, make sure that you
change the `.in` files and then run `make` to regenerate the
pre-processed versions before doing any testing. To avoid forgetting
(which can potentially waste a lot of time debugging the wrong code),
you can automatically run `make` in an infinite loop every second via:
make watch
(You could even use fancier approaches like
[`inotifywait(1)`](https://www.man7.org/linux/man-pages/man1/inotifywait.1.html)
or [Guard](https://guardgem.org/). But those are probably overkill in
this case where the simple `while` loop is plenty good enough.)
Testing
~~~~~~~
The test suite can be found in the [`t/`](t/) subdirectory. You can
run the test suite via:
make check
Tests can be run individually as follows. First you have to ensure
that the `t/`, `bin/`, and `lib/` directories are on Perl's search path.
Assuming that you run all tests from the root of the repository tree,
this will do the job:
export PERL5LIB=t:bin:lib
(Not all tests require all of these, but it's safer to include all of
them.)
Secondly, be aware that if you want to test modifications to the
source files, you will need to run `make watch`, or `make` before each
test run as explained above.
Now running an individual test is as simple as:
perl t/chkstow.t
or with a given debugging verbosity corresponding to the `-v` / `--verbose`
command-line option:
TEST_VERBOSE=4 perl t/chkstow.t
The [`prove(1)` test runner](https://perldoc.perl.org/prove) is another
good alternative which provides several handy extra features. Invocation
is very similar, e.g.:
prove t/stow.t
or to run the whole suite:
prove
However currently there is an issue where this interferes with
`TEST_VERBOSE`.
If you want to create test files for experimentation, it is
recommended to put them in a subdirectory called `playground/` since
this will be automatically ignored by git and the build process,
avoiding any undesirable complications.
Test coverage
~~~~~~~~~~~~~
To view test coverage reports, first ensure that
[`Devel::Cover`](https://metacpan.org/dist/Devel-Cover) is installed.
Then type `make coverage`. The last lines of the output should
include something like:
HTML output written to /home/user/path/to/stow/cover_db/coverage.html
which you can open in a web browser to view the report.
Translating Stow
----------------
Stow is not currently multi-lingual, but patches would be very
gratefully accepted. Please e-mail
[stow-devel](https://lists.gnu.org/mailman/listinfo/stow-devel) if you
intend to work on this.
Maintainers
-----------
Stow is currently being maintained by Adam Spiers. Please use [the
mailing lists](README.md#mailing-lists).
Helping the GNU project
-----------------------
For more general information, please read [How to help
GNU](https://www.gnu.org/help/).

849
COPYING
View file

@ -1,285 +1,626 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
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
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
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.
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to 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.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must 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.
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
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.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
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.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
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.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
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".
TERMS AND CONDITIONS
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.
0. Definitions.
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.
"This License" refers to version 3 of the GNU General Public License.
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.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
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:
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
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.
A "covered work" means either the unmodified Program or a work based
on the Program.
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.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
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.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
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.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
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:
1. Source Code.
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,
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
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,
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
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 "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The 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.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
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.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
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.
The Corresponding Source for a work in source code form is that
same work.
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
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
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
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
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.
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
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.
13. Use with the GNU Affero General Public License.
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.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
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.
14. Revised Versions 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
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies 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.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
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.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
NO WARRANTY
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
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.
15. Disclaimer of Warranty.
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.
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.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@ -287,15 +628,15 @@ 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
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@ -304,36 +645,30 @@ the "copyright" line and a pointer to where the full notice is found.
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.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
If the program does terminal interaction, 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'.
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, 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.
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
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.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

108
INSTALL
View file

@ -1,108 +0,0 @@
Basic Installation
==================
Stow is a Perl script. You must have Perl 4 or Perl 5 in order for it
to run.
The steps in building stow are:
1. `cd' to the directory containing the source code (and this file)
and type `./configure' to configure stow for your system. This
step will attempt to locate your copy of perl and use its location
to create `stow' from `stow.in'. If perl can't be found, you'll
have to edit line 1 of `stow' from `#!false' to `#!/path/to/perl'
(where /path/to/perl is wherever perl will be found when stow
runs).
2. Type `make' to create stow.info from stow.texi.
3. Type `make install' to install `stow' and `stow.info'.
4. You can remove the generated files from the source code directory
by typing `make clean'. To also remove the files that `configure'
created (so you can compile the package for a different computer),
type `make distclean'. There is also a `make maintainer-clean'
target, but that is intended mainly for stow's developers. If you
use it, you may have to get all sorts of other programs in order
to regenerate files that came with the distribution.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin' and `/usr/local/info'. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure'
the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Since `stow' is concerned with separating a package's installation
tree from its run-time tree, you might want to install `stow' into a
directory such as `/usr/local/stow/stow' but have it run out of
`/usr/local'. Do this by giving the run-time prefix (e.g.,
/usr/local) to configure as described above; then run `make'; then run
`make install prefix=/usr/local/stow/stow'. For more information on
this technique, see the Stow manual.
The configuration system
========================
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' and to create the `stow' script
itself, using Makefile.in and stow.in as templates. Finally, it
creates a shell script `config.status' that you can run in the future
to recreate the current configuration, a file `config.cache' that
saves the results of its tests to speed up reconfiguring, and a file
`config.log' containing other output.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need `configure.in' if you want to change
it or regenerate `configure' using a newer version of `autoconf'.
The file `Makefile.am' is used to create `Makefile.in' by a program
called `automake'. You only need `Makefile.am' if you want to change
it or regenerate `Makefile.in' using a newer version of `automake'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Operation Controls
==================
`configure' recognizes the following options to control how it
operates.
`--cache-file=FILE'
Use and save the results of the tests in FILE instead of
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
debugging `configure'.
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made.
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`configure' also accepts some other, not widely useful, options.

202
INSTALL.md Normal file
View file

@ -0,0 +1,202 @@
How to install GNU Stow
=======================
Prerequisites
-------------
Stow is a collection of Perl scripts and modules. You must have Perl
5.6.1 or later in order for it to run. The test suite also requires
the `Test::More` and `Test::Output` modules which can be obtained from
CPAN. They are also available as packages in some of the GNU/Linux
distributions.
Installation methods
--------------------
Stow can either be installed via the standard GNU Autotools procedure
(`./configure && make install`) or since 2.1.0, via CPAN-style via
Module::Build.
Advantages of the Autotools approach:
- It's arguably more flexible.
- It will install the documentation in Info, HTML, man, and PDF
formats.
Advantages of the `Module::Build` approach:
- It's more in keeping with the standard way to distribute CPAN
modules.
- It performs dependency checking to ensure you have the necessary
Perl modules installed.
Both approaches are described in detail below. However if you are
building from the git repository rather than an official release,
you first need to perform some extra steps:
Preparatory steps required only when building from git
------------------------------------------------------
`configure` and `Makefile` are included in official releases of Stow,
but they are deliberately omitted from the git repository because they
are autogenerated. Therefore if you are installing directly from git,
you first need to generate them as follows.
First `cd` to the directory containing the source code (and this
file), and then run:
autoreconf -iv
If this runs successfully then you are ready to continue with one of
the two installation methods below.
Basic Installation via `Module::Build`
--------------------------------------
The steps in building Stow are:
1. `cd` to the directory containing the source code (and this file).
2. If you are building from an official GNU release tarball, type
`./configure && make` to configure stow for your system. If you
are building from a CPAN tarball, this step can be skipped.
If `make` warns that the Perl module installation directory is
not in `@INC`, then you should run:
eval `perl -V:siteprefix`
./configure --prefix=$siteprefix && make
to avoid a superfluous `use lib` line in your stow executable.
3. Type `perl Build.PL`.
4. Type `./Build install` to install the various files. As noted
above, this installs fewer files than the Autotools installation.
Basic Installation via Autotools
--------------------------------
The steps in building Stow are:
1. `cd` to the directory containing the source code (and this file).
2. Type `./configure` to configure stow for your system. This step
will attempt to locate your copy of perl and set its location in
`Makefile.in`. You can use the normal arguments to change the
default installation paths (see below); additionally you can use
the
--with-pmdir=/path/to/perl/modules
option to manually choose where the Perl modules get installed.
However, if you don't, the `configure` script will go to great
lengths to try to choose a sensible default.
3. Type `make install` to install the various files. If the chosen
installation directory for Perl modules is not included in Perl's
built-in `@INC` search path, the Makefile rules will automatically
insert a
use lib "...";
line into the generated stow script to ensure that it can always
locate the Perl modules without needing to manually set `PERL5LIB`.
4. You can remove the generated files from the source code directory
by typing `make clean`. To also remove the files that `configure`
created (so you can compile the package for a different computer),
type `make distclean`. There is also a `make maintainer-clean`
target, but that is intended mainly for stow's developers. If you
use it, you may have to get all sorts of other programs in order
to regenerate files that came with the distribution.
Installation Names
------------------
By default, `make install` will install the package's files in
`/usr/local/bin` and `/usr/local/info`. You can specify an
installation prefix other than `/usr/local` by giving `configure` the
option `--prefix=PATH`.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure`
the option `--program-prefix=PREFIX` or `--program-suffix=SUFFIX`.
Since `stow` is concerned with separating a package's installation
tree from its run-time tree, you might want to install `stow` into a
directory such as `/usr/local/stow/stow` but have it run out of
`/usr/local`. Do this by giving the run-time prefix (e.g.,
/usr/local) to configure as described above; then run `make`; then run
`make install prefix=/usr/local/stow/stow`. For more information on
this technique, see the Stow manual.
The configuration system
------------------------
The `configure` shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile` and to create the `stow` script
itself, using Makefile.in and stow.in as templates. Finally, it
creates a shell script `config.status` that you can run in the future
to recreate the current configuration, a file `config.cache` that
saves the results of its tests to speed up reconfiguring, and a file
`config.log` containing other output.
The file `configure.ac` is used to create `configure` by a program
called `autoconf`. You only need `configure.ac` if you want to change
it or regenerate `configure` using a newer version of `autoconf`.
The file `Makefile.am` is used to create `Makefile.in` by a program
called `automake`. You only need `Makefile.am` if you want to change
it or regenerate `Makefile.in` using a newer version of `automake`.
Sharing Defaults
----------------
If you want to set default values for `configure` scripts to share,
you can create a site shell script called `config.site` that gives
default values for variables like `CC`, `cache_file`, and `prefix`.
`configure` looks for `PREFIX/share/config.site` if it exists, then
`PREFIX/etc/config.site` if it exists. Or, you can set the
`CONFIG_SITE` environment variable to the location of the site script.
A warning: not all `configure` scripts look for a site script.
Operation Controls
------------------
`configure` recognizes the following options to control how it
operates.
`--cache-file=FILE`
Use and save the results of the tests in FILE instead of
`./config.cache`. Set FILE to `/dev/null` to disable caching, for
debugging `configure`.
`--help`
Print a summary of the options to `configure`, and exit.
`--quiet`
`--silent`
`-q`
Do not print messages saying which checks are being made.
`--srcdir=DIR`
Look for the package's source code in directory DIR. Usually
`configure` can determine that directory automatically.
`--version`
Print the version of Autoconf used to generate the `configure`
script, and exit.
`configure` also accepts some other, not widely useful, options.
License for this file
---------------------
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.

57
MANIFEST Normal file
View file

@ -0,0 +1,57 @@
AUTHORS
aclocal.m4
automake/install-sh
automake/mdate-sh
automake/missing
automake/texinfo.tex
bin/chkstow
bin/chkstow.in
bin/stow
bin/stow.in
Build.PL
ChangeLog
configure
configure.ac
CONTRIBUTING.md
COPYING
default-ignore-list
doc/ChangeLog.OLD
doc/manual-single.html
doc/manual.pdf
doc/stow.8
doc/stow.info
doc/stow.texi
doc/version.texi
INSTALL.md
lib/Stow.pm
lib/Stow.pm.in
lib/Stow/Util.pm
lib/Stow/Util.pm.in
Makefile.am
Makefile.in
MANIFEST This list of files
MANIFEST.SKIP
NEWS
README.md
t/chkstow.t
t/cleanup_invalid_links.t
t/cli.t
t/cli_options.t
t/defer.t
t/dotfiles.t
t/examples.t
t/find_stowed_path.t
t/foldable.t
t/ignore.t
t/join_paths.t
t/link_dest_within_stow_dir.t
t/parent.t
t/stow.t
t/rc_options.t
t/testutil.pm
t/unstow.t
tools/get-version
THANKS
TODO
META.yml
META.json

96
MANIFEST.SKIP Normal file
View file

@ -0,0 +1,96 @@
#!start included /usr/share/perl5/ExtUtils/MANIFEST.SKIP
# Avoid version control files.
\bRCS\b
\bCVS\b
\bSCCS\b
,v$
\B\.svn\b
\B\.git\b
\B\.gitignore\b
\b_darcs\b
\B\.cvsignore$
^\.mrdownload$
# Avoid VMS specific MakeMaker generated files
\bDescrip.MMS$
\bDESCRIP.MMS$
\bdescrip.mms$
# Avoid Makemaker generated and utility files.
\bMANIFEST\.bak
\bMakefile$
\bblib/
\bMakeMaker-\d
\bpm_to_blib\.ts$
\bpm_to_blib$
\bblibdirs\.ts$ # 6.18 through 6.25 generated this
# Avoid Module::Build generated and utility files.
\bBuild$
\b_build/
\bBuild.bat$
\bBuild.COM$
\bBUILD.COM$
\bbuild.com$
# Avoid temp and backup files.
~$
\.old$
\#$
\b\.#
\.bak$
\.tmp$
\.#
\.rej$
\.orig$
# Avoid OS-specific files/dirs
# Mac OSX metadata
\B\.DS_Store
# Mac OSX SMB mount metadata files
\B\._
# Avoid Devel::Cover files.
\bcover_db\b
#!end included /usr/share/perl5/ExtUtils/MANIFEST.SKIP
# Avoid configuration metadata file
^MYMETA\.
# Avoid Module::Build generated and utility files.
\bBuild$
\bBuild.bat$
\b_build
\bBuild.COM$
\bBUILD.COM$
\bbuild.com$
^MANIFEST\.SKIP
# Avoid archives of this distribution
\b[sS]tow-v?[\d\.\_]+
# Avoid autotools stuff
^aclocal.m4$
^automake/
^autom4te\.cache/.+$
^config\.(log|status)$
^doc/\.dirstamp$
^doc/manual-single-old-texi2html\.html
^doc/manual-single-texi2html-wrapper\.html
^doc/manual-split/
^doc/stamp-vti$
^doc/HOWTO-RELEASE$
# Avoid test files
tmp-testing-trees*
^.coveralls.yml
^.github/workflows/
^.travis.yml
^docker/
^[a-zA-Z]*-docker.sh
^playground/
# Avoid development config
^.dir-locals.el
^.dumbjump

60
META.json Normal file
View file

@ -0,0 +1,60 @@
{
"abstract" : "manage farms of symbolic links",
"author" : [
"unknown"
],
"dynamic_config" : 1,
"generated_by" : "Module::Build version 0.4234",
"license" : [
"gpl_1"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "Stow",
"prereqs" : {
"build" : {
"requires" : {
"IO::Scalar" : "0",
"Test::More" : "0",
"Test::Output" : "0"
}
},
"configure" : {
"requires" : {
"Module::Build" : "0"
}
},
"runtime" : {
"requires" : {
"Carp" : "0",
"IO::File" : "0",
"perl" : "5.006"
}
}
},
"provides" : {
"Stow" : {
"file" : "lib/Stow.pm",
"version" : "v2.4.0"
},
"Stow::Util" : {
"file" : "lib/Stow/Util.pm",
"version" : "v2.4.0"
}
},
"release_status" : "stable",
"resources" : {
"homepage" : "https://savannah.gnu.org/projects/stow",
"license" : [
"http://www.gnu.org/licenses/gpl-2.0.html"
],
"repository" : {
"type" : "git",
"url" : "git://git.savannah.gnu.org/stow.git"
}
},
"version" : "v2.4.0",
"x_serialization_backend" : "JSON::PP version 4.16"
}

34
META.yml Normal file
View file

@ -0,0 +1,34 @@
---
abstract: 'manage farms of symbolic links'
author:
- unknown
build_requires:
IO::Scalar: '0'
Test::More: '0'
Test::Output: '0'
configure_requires:
Module::Build: '0'
dynamic_config: 1
generated_by: 'Module::Build version 0.4234, CPAN::Meta::Converter version 2.150010'
license: gpl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: Stow
provides:
Stow:
file: lib/Stow.pm
version: v2.4.0
Stow::Util:
file: lib/Stow/Util.pm
version: v2.4.0
requires:
Carp: '0'
IO::File: '0'
perl: '5.006'
resources:
homepage: https://savannah.gnu.org/projects/stow
license: http://www.gnu.org/licenses/gpl-2.0.html
repository: git://git.savannah.gnu.org/stow.git
version: v2.4.0
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

View file

@ -1,18 +1,332 @@
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
## Process this file with Automake to produce Makefile.in
AUTOMAKE_OPTIONS = dist-shar
bin_SCRIPTS = bin/stow bin/chkstow
info_TEXINFOS = doc/stow.texi
dist_man_MANS = doc/stow.8
PDF = doc/manual.pdf
HTML = doc/manual-single.html
dist_doc_DATA = \
README.md INSTALL.md \
$(PDF) $(HTML) doc/version.texi \
ChangeLog doc/ChangeLog.OLD
bin_SCRIPTS = stow
info_TEXINFOS = stow.texi
# automake magic to define where *_DATA files get installed:
pmdir = $(PMDIR)
pmstowdir = $(pmdir)/Stow
CLEANFILES = stow manual.html manual.texi
pm_DATA = lib/Stow.pm
pmstow_DATA = lib/Stow/Util.pm
# The rules for manual.html and manual.texi are only used by
# the developer
manual.html: manual.texi
export TEXI2DVI_BUILD_MODE = clean
AM_MAKEINFOFLAGS = -I $(srcdir)
# We require this -I parameter to ensure that the include of the
# default ignore list in the manual works correctly, even when the
# manual is being built via make distcheck from a different directory.
# Unfortunately this is the only way to do it:
#
# https://lists.gnu.org/archive/html/bug-automake/2008-09/msg00040.html
#
# even though it annoyingly produces a warning with the -Wall option
# to AM_INIT_AUTOMAKE which has to be silenced via -Wno-override.
TEXI2DVI = texi2dvi $(AM_MAKEINFOFLAGS)
DEFAULT_IGNORE_LIST = $(srcdir)/default-ignore-list
doc_deps = $(info_TEXINFOS) doc/version.texi $(DEFAULT_IGNORE_LIST)
TESTS_DIR = $(srcdir)/t
TESTS_OUT = tmp-testing-trees tmp-testing-trees-compat
TESTS_ENVIRONMENT = $(PERL) -Ibin -Ilib -I$(TESTS_DIR)
# This is a kind of hack; TESTS needs to be set to ensure that the
# `check-am' target makes check-TESTS, but we override check-TESTS
# so it doesn't really matter what it's set to, as long as it already
# exists (otherwise automake will try to build it).
TESTS = t
# GNU autotools standardised on the 'check' target, but CPAN (and the
# rest of the world) standardised on the 'test' target.
test: check
# required in vpath mode to ensure $build/t/ exists
check_DATA = $(TESTS_OUT)
# Test::Harness produces cleaner output than automake's default test
# harness, albeit without the pretty colours provided by the
# `color-tests' AM_INIT_AUTOMAKE option. This also dodges having to
# set TESTS to the full list of tests, which is good because automake
# doesn't support wildcards, and so it would be too easy to forget to
# add a new one to the list.
#
# Note that automake's `check' rule cannot be overridden
# for some weird reason:
#
# https://lists.gnu.org/archive/html/automake/2011-09/msg00029.html
#
# so we override check-TESTS instead which is where the real work is
# done anyway. Unfortunately this produces a warning with the -Wall
# option to AM_INIT_AUTOMAKE which has to be silenced via
# -Wno-override.
check-TESTS:
dir=$(TESTS_DIR); \
$(TESTS_ENVIRONMENT) -MTest::Harness -e 'runtests(@ARGV)' "$${dir#./}"/*.t
coverage:
PERL5OPT=-MDevel::Cover $(MAKE) check-TESTS
cover
$(TESTS_OUT):
mkdir -p $@
CPAN_FILES = MANIFEST MANIFEST.SKIP Build.PL META.yml META.json
EXTRA_DIST = \
bin/stow.in bin/chkstow.in lib/Stow.pm.in lib/Stow/Util.pm.in \
doc/manual-split \
$(TESTS) t/testutil.pm \
$(DEFAULT_IGNORE_LIST) \
$(CPAN_FILES)
CLEANFILES = $(bin_SCRIPTS) $(pm_DATA) $(pmstow_DATA)
DISTCLEANFILES = Makefile.in configure Build MYMETA.*
MAINTAINERCLEANFILES = $(dist_man_MANS) $(HTML) $(PDF) ChangeLog
# clean up auto-generated files
clean-local:
-rm -rf $(TESTS_OUT)
maintainer-clean-local:
-rm -rf doc/manual-split cover_db
# this is more explicit and reliable than the config file trick
edit = sed -e 's|[@]PERL[@]|$(PERL)|g' \
-e 's|[@]VERSION[@]|$(VERSION)|g' \
-e "s|[@]USE_LIB_PMDIR[@]|$$use_lib_pmdir|g"
pmdir_in_INC = \
PERL5LIB= $(PERL) -V | \
awk '/@INC/ {p=1; next} p==1 {print $$1}' | \
grep -F -x -q "$(pmdir)"
calc_use_lib_pmdir = \
pmdir="$(pmdir)"; \
if [ $(FINDBIN) = yes ]; then \
use_lib_pmdir="use FindBin; use lib \"\$$FindBin::Bin/../$${pmdir\#$(prefix)/}\";"; \
elif $(pmdir_in_INC); then \
use_lib_pmdir=""; \
else \
use_lib_pmdir="use lib \"$(pmdir)\";"; \
fi
check_pmdir = \
echo; \
echo "\# Perl modules will be installed to $(pmdir)"; \
echo "\# "; \
if $(pmdir_in_INC); then \
echo "\# This is in $(PERL)'s built-in @INC, so everything"; \
echo "\# should work fine with no extra effort."; \
else \
echo "\# This is not in $(PERL)'s built-in @INC, so the"; \
echo "\# front-end scripts will have an appropriate \"use lib\""; \
echo "\# line inserted to compensate."; \
fi; \
echo
bin/stow: bin/stow.in Makefile.am
[ -d bin ] || mkdir bin # required in vpath mode
@$(check_pmdir)
@$(calc_use_lib_pmdir); \
$(edit) < $< > $@
@echo "Generated $@ from $<"
chmod +x $@
bin/chkstow: bin/chkstow.in Makefile.am
@[ -d bin ] || mkdir bin # required in vpath mode
@$(edit) < $< > $@
@echo "Generated $@ from $<"
chmod +x $@
lib/Stow.pm: lib/Stow.pm.in $(DEFAULT_IGNORE_LIST) Makefile.am
@[ -d lib ] || mkdir lib # required in vpath mode
@( $(edit) < $<; cat $(DEFAULT_IGNORE_LIST) ) > $@
@echo "Generated $@ from $< and $(DEFAULT_IGNORE_LIST)"
lib/Stow/Util.pm: lib/Stow/Util.pm.in Makefile.am
@[ -d lib/Stow ] || mkdir -p lib/Stow # required in vpath mode
@$(edit) < $< > $@
@echo "Generated $@ from $<"
##############################################################################
# The below rules should only be needed by developers.
##############################################################################
cpanm:
cpanm --quiet --installdeps --notest .; \
CPANM_RESULT=$$?; \
if [ $$CPANM_RESULT != 0 ]; then \
echo ---------------------------------------------------; \
cat ~/.cpanm/build.log; \
echo ---------------------------------------------------; \
exit $$CPANM_RESULT; \
fi
doc/stow.8: bin/stow.in Makefile.am
[ -d doc ] || mkdir doc # required in vpath mode
$(edit) < $< | pod2man --name stow --section 8 > $@
# We use automake's built-in rule to generate stow.info. The built-in
# rules would also generate doc/stow.html and doc/stow.pdf, but after
# installation we want $(docdir) (typically /usr/share/doc/stow/) to
# contain manual-single.html, manual.pdf, and manual-split/*.html, to
# make it explicitly obvious that these files contain the user manual
# rather than some other Stow-related documentation.
#
# If it were not for a troublesome dependency on doc/$(am__dirstamp):
#
# https://lists.gnu.org/archive/html/automake/2011-11/msg00107.html
#
# we could have achieved this using the built-in rules combined with
# install-data-hook to rename from stow.pdf to manual.pdf etc. on
# install. Instead, by overriding the built-in rules with modified
# versions, we can kill both birds with one stone.
# Generating the single-page HTML version used to be done with the old
# texi2html, which is no longer maintained. This rule is not used,
# but is kept to allow comparing of results during the transition, and
# potentially longer for posterity.
doc/manual-single-old-texi2html.html: $(doc_deps)
texi2html --P=$(srcdir) --output=$@ -expandinfo -menu -monolithic -verbose $<
# One alternative to the old texi2html approach is to use the texi2html
# wrapper around texi2any which is provided as a partial drop-in replacement:
#
# https://www.gnu.org/software/texinfo/manual/texinfo/html_node/texi2html.html#texi2html
#
# Differences to the old texi2html:
#
# - Doesn't wrap @file{foo} paths with quotes, which looks better.
# - Missing certain sections
doc/manual-single-texi2html-wrapper.html: $(doc_deps)
texi2any -P $(srcdir) --output=$@ --verbose \
-c TEXI2HTML=1 -c SHOW_MENU=1 -c MONOLITHIC=1 $<
.PHONY: manual-single-html-all
manual-single-html-all: \
$(HTML) \
doc/manual-single-texi2html-wrapper.html \
doc/manual-single-old-texi2html.html
# Modern approach using $(MAKEINFOHTML) --no-split
# Differences to the older two approaches:
#
# - Nicer navigation links between sections
$(HTML): $(doc_deps)
[ -d doc ] || mkdir doc # required in vpath mode
-rm -f $@
texi2html -expandinfo -menu -monolithic -verbose $<
$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I doc -I $(srcdir)/doc \
-c USE_TITLEPAGE_FOR_TITLE=1 --no-split -o $@ \
`test -f 'doc/stow.texi' || echo '$(srcdir)/'`doc/stow.texi
manual.texi: stow.texi
-rm -f $@
cp $< $@
$(PDF): $(doc_deps)
TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \
MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I doc -I $(srcdir)/doc' \
$(TEXI2PDF) -o $@ `test -f 'doc/stow.texi' || echo '$(srcdir)/'`doc/stow.texi
doc/manual-split: $(doc_deps)
rm -rf $@.new
if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I doc -I $(srcdir)/doc \
-o $@.new `test -f 'doc/stow.texi' || echo '$(srcdir)/'`doc/stow.texi; \
then \
rm -rf $@; \
mv $@.new $@; \
else \
rm -Rf $@.new $@; \
exit 1; \
fi
# The split version of the manual is copied to $(docdir)/manual-split
# by install-data-hook. The whole subdirectory is included via
# EXTRA_DIST in order to avoid having to list each file explicitly in
# dist_doc_DATA, since automake doesn't support wildcards, and
# dist_doc_DATA cannot refer to directories while EXTRA_DIST can (go
# figure ...)
install-data-hook: doc/manual-split
cp -r $(srcdir)/doc/manual-split $(DESTDIR)$(docdir)
uninstall-hook:
chmod u+w -R $(DESTDIR)$(docdir)/manual-split
rm -rf $(DESTDIR)$(docdir)/manual-split
# Using install-data-hook has the slightly annoying disadvantage that
# by default the split version of the manual isn't automatically
# rebuilt during development by a simple `make'. A workaround hack
# for this is to piggy-back the dependency onto manual-single.html,
# which *is* automatically rebuilt by `make':
$(HTML): doc/manual-split
# With the above hack, this probably isn't necessary but is safer to
# keep in anyway:
dist-hook: doc/manual-split
dist-hook: $(dist_man_MANS)
## If we are creating a distribution from a git checkout, ensure
## the ChangeLog file is in sync the git repository.
if test -d $(top_srcdir)/.git; then \
rm -f ChangeLog \
&& $(MAKE) $(AM_MAKEFLAGS) ChangeLog \
&& cp -f ChangeLog $(distdir)/ChangeLog; \
fi
ChangeLog: doc/ChangeLog.OLD
@if [ -d .git ]; then \
( \
git log \
--format="format:%ad %aN <%aE>%n%n * %w(70,0,4)%s%+b%n" \
--name-status \
v2.0.2..HEAD \
| sed 's/^\([A-Z]\)\t/ \1 /'; \
cat $< \
) > $@; \
echo "Rebuilt $@ from git commit history."; \
else \
echo "Not in a git repository; can't update ChangeLog."; \
fi
# Watch for changes, and if any rebuilds are required, also do a
# make install.
#
# If we solved https://github.com/aspiers/stow/issues/84, we could
# probably ditch this:
watch:
@echo "Watching for changes to program source files ..."
@while true; do \
if $(MAKE) 2>&1 | \
grep -vE 'make\[[1-9]\]: (Entering|Leaving) directory ' | \
grep -v 'Nothing to be done'; \
then \
echo; \
echo "-----------------------------------------------------"; \
echo "make found things to rebuild; doing $(MAKE) install ..."; \
echo; \
$(MAKE) install; \
echo; \
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"; \
echo; \
fi; \
sleep 1; \
done 2>&1 | \
grep -vE 'make\[[1-9]\]: (Entering|Leaving) directory '

View file

@ -1,279 +0,0 @@
# Makefile.in generated automatically by automake 1.0 from Makefile.am
# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
SHELL = /bin/sh
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = .
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
AUTOMAKE_OPTIONS = dist-shar
bin_SCRIPTS = stow
info_TEXINFOS = stow.texi
CLEANFILES = stow manual.html manual.texi
ACLOCAL = aclocal.m4
mkinstalldirs = $(top_srcdir)/mkinstalldirs
SCRIPTS = $(bin_SCRIPTS)
MAKEINFO = makeinfo
TEXI2DVI = texi2dvi
INFOS = stow.info*
INFO_DEPS = stow.info
DVIS = stow.dvi
TEXINFOS = stow.texi
DIST_COMMON = README AUTHORS COPYING ChangeLog INSTALL Makefile.am \
Makefile.in NEWS README THANKS TODO aclocal.m4 configure configure.in \
install-sh mdate-sh mkinstalldirs stamp-vti stow.in texinfo.tex \
version.texi
PACKAGE = @PACKAGE@
VERSION = @VERSION@
DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
$(TEXINFOS) $(INFOS) $(MANS) $(EXTRA_DIST) $(DATA)
DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
$(TEXINFOS) $(INFO_DEPS) $(MANS) $(EXTRA_DIST) $(DATA)
TAR = tar
default: all
$(srcdir)/Makefile.in: Makefile.am configure.in
cd $(srcdir) && automake Makefile
# For an explanation of the following Makefile rules, see node
# `Automatic Remaking' in GNU Autoconf documentation.
Makefile: Makefile.in config.status
CONFIG_FILES=$@ CONFIG_HEADERS= ./config.status
config.status: configure
./config.status --recheck
$(srcdir)/configure: configure.in $(ACLOCAL) $(CONFIGURE_DEPENDENCIES)
cd $(srcdir) && autoconf
stow: $(top_builddir)/config.status stow.in
cd $(top_builddir) && CONFIG_FILES=$@ CONFIG_HEADERS= ./config.status
install-binSCRIPTS: $(bin_SCRIPTS)
$(mkinstalldirs) $(bindir)
list="$(bin_SCRIPTS)"; for p in $$list; do \
if test -f $$p; then \
$(INSTALL_SCRIPT) $$p $(bindir)/`echo $$p|sed '$(transform)'`; \
else if test -f $(srcdir)/$$p; then \
$(INSTALL_SCRIPT) $(srcdir)/$$p \
$(bindir)/`echo $$p|sed '$(transform)'`; \
else :; fi; fi; \
done
uninstall-binSCRIPTS:
list="$(bin_SCRIPTS)"; for p in $$list; do \
rm -f $(bindir)/`echo $$p|sed '$(transform)'`; \
done
version.texi: stamp-vti
stamp-vti: stow.texi $(top_srcdir)/configure.in
echo "@set UPDATED `cd $(srcdir) \
&& $(SHELL) ./mdate-sh stow.texi`" > vti.tmp
echo "@set EDITION $(VERSION)" >> vti.tmp
echo "@set VERSION $(VERSION)" >> vti.tmp
if cmp -s vti.tmp $(srcdir)/version.texi; then \
rm vti.tmp; \
else \
mv vti.tmp $(srcdir)/version.texi; \
fi
echo timestamp > $(srcdir)/stamp-vti
mostlyclean-vti:
rm -f vti.tmp
clean-vti:
distclean-vti:
maintainer-clean-vti:
rm -f stamp-vti version.texi
stow.info: stow.texi version.texi
.texi.info:
$(MAKEINFO) -I$(srcdir) $< -o $(srcdir)/$@
.texi.dvi:
TEXINPUTS=$(srcdir):$$TEXINPUTS $(TEXI2DVI) $<
install-info: $(INFO_DEPS)
$(mkinstalldirs) $(infodir)
for file in $(INFO_DEPS); do \
for ifile in `cd $(srcdir) && echo $$file*`; do \
$(INSTALL_DATA) $(srcdir)/$$ifile $(infodir)/$$ifile; \
done; \
done
uninstall-info:
cd $(srcdir) && for file in *.info*; do \
rm -f $(infodir)/$$file; \
done
mostlyclean-info:
rm -f stow.aux stow.cp stow.cps stow.dvi stow.fn stow.fns stow.ky \
stow.log stow.pg stow.toc stow.tp stow.vr stow.op
clean-info:
distclean-info:
maintainer-clean-info:
rm -f $(INFOS)
tags: TAGS
TAGS:
distdir = $(PACKAGE)-$(VERSION)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
rm -rf $(distdir)
$(TAR) zxf $(distdir).tar.gz
mkdir $(distdir)/=build
mkdir $(distdir)/=inst
dc_install_base=`cd $(distdir)/=inst && pwd`; \
cd $(distdir)/=build \
&& ../configure --srcdir=.. --prefix=$$dc_install_base \
&& $(MAKE) \
&& $(MAKE) check \
&& $(MAKE) install \
&& $(MAKE) installcheck \
&& $(MAKE) dist
rm -rf $(distdir)
@echo "========================"; \
echo "$(distdir).tar.gz is ready for distribution"; \
echo "========================"
dist: distdir
chmod -R a+r $(distdir)
$(TAR) chozf $(distdir).tar.gz $(distdir)
rm -rf $(distdir)
dist-shar: distdir
chmod -R a+r $(distdir)
shar $(distdir) | gzip > $(distdir).shar.gz
rm -rf $(distdir)
distdir: $(DEP_DISTFILES)
rm -rf $(distdir)
mkdir $(distdir)
chmod 777 $(distdir)
@for file in `cd $(srcdir) && echo $(DISTFILES)`; do \
test -f $(distdir)/$$file \
|| ln $(srcdir)/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $(srcdir)/$$file $(distdir)/$$file; \
done
info: $(INFO_DEPS)
dvi: $(DVIS)
check: all
installcheck:
install-exec: install-binSCRIPTS
install-data: install-info
install: install-exec install-data all
@:
uninstall: uninstall-binSCRIPTS uninstall-info
all: $(INFO_DEPS) $(SCRIPTS) Makefile
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
installdirs:
$(mkinstalldirs) $(bindir) $(infodir)
mostlyclean-generic:
test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
clean-generic:
test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
distclean-generic:
rm -f Makefile $(DISTCLEANFILES)
rm -f config.cache config.log $(CONFIG_HEADER) stamp-h
maintainer-clean-generic:
test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
mostlyclean: mostlyclean-vti mostlyclean-info mostlyclean-generic
clean: clean-vti clean-info clean-generic mostlyclean
distclean: distclean-vti distclean-info distclean-generic clean
rm -f config.status
maintainer-clean: maintainer-clean-vti maintainer-clean-info \
maintainer-clean-generic distclean
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
rm -f config.status
.PHONY: default uninstall-binSCRIPTS install-binSCRIPTS mostlyclean-vti \
distclean-vti clean-vti maintainer-clean-vti install-info \
uninstall-info mostlyclean-info distclean-info clean-info \
maintainer-clean-info tags distdir info dvi check installcheck \
install-exec install-data install uninstall all installdirs \
mostlyclean-generic distclean-generic clean-generic \
maintainer-clean-generic clean mostlyclean distclean maintainer-clean
# The rules for manual.html and manual.texi are only used by
# the developer
manual.html: manual.texi
-rm -f $@
texi2html -expandinfo -menu -monolithic -verbose $<
manual.texi: stow.texi
-rm -f $@
cp $< $@
.SUFFIXES:
.SUFFIXES: .texi .info .dvi
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

679
NEWS
View file

@ -1,22 +1,667 @@
News file for Stow.
* Changes in version 1.3:
** Added --restow option.
** Fixed handling of slashes in package names.
** Expanded configure-time search for Perl binary.
* Changes in version 2.4.0
* Changes in version 1.2:
** Dependency on `pwd' removed.
** Perl 4 compatibility fixes.
** Manual expanded even more.
*** --dotfiles now works with directories
* Changes in version 1.1:
** Long and short options now accepted.
** Manual expanded.
** `make clean' removes stow (which is generated from stow.in).
A long-standing bug preventing the --dotfiles option from working
correctly with directories has been fixed.
* Initial public release (v1.0) of Stow.
Local variables:
mode: outline
End:
It should also works in combination with the --compat option.
*** Eliminated a spurious warning on unstowing
2.3.1 introduced a benign but annoying warning when unstowing
in certain circumstances. It looked like:
BUG in find_stowed_path? Absolute/relative mismatch between Stow dir X and path Y
This was caused by erroneous logic, and has now been fixed.
*** Unstowing logic has been improved in other cases
Several other improvements have been made internally to the
unstowing logic. These changes should all be either invisible
(except for changes to debug output) or improvements, but if you
encounter any unexpected behaviour, please report it as directed
in the manual.
*** Improved debug output
Extra output resulting from use of the -v / --verbose flag
now appears in a more logical and understandable way.
*** Janitorial tasks
Users are not substantially affected by these changes.
***** Added some more information from the web page to the README
***** Made some improvements to the documentation
***** Improve readability of source code
Quite a few extra details have been added in comments to clarify
how the code works. Many variable names have also been
improved. The comments of many Stow class methods have been
converted into Perl POD format.
***** Added a =CONTRIBUTING.md= file
***** Add a =watch= target to =Makefile=
=make watch= provides easy continual pre-processing during
development, which reduces the risk of debugging the wrong code.
***** Removed texinfo.tex from the distribution
This eliminates existing and future bit-rot.
***** Updated aclocal.m4 from 1.15.1 to 1.16.5
This mostly just updates copyright notices to 2021, and URLs to https.
***** Replace broken gmane links with links to lists.gnu.org
[[https://lars.ingebrigtsen.no/2020/01/06/whatever-happened-to-news-gmane-org/][gmane has been dead for quite a while.]]
***** Improve support for navigating / editing source via emacs
******* Support source navigation in emacs via [[https://github.com/jacktasia/dumb-jump][dumb-jump]].
******* Configure cperl-mode to match existing coding style.
*** Various maintainer tweaks
Further improved the release process and its documentation in
various minor ways.
* Changes in version 2.3.1
*** Remove dependencies on Hash::Merge and Clone::Choose
stow 2.3.0 added external runtime dependencies on Hash::Merge and
Clone::Choose. Historically stow hasn't had runtime dependencies
other than Perl itself, which is a useful property if you're
managing the installation of Perl using stow; the bootstrapping
instructions in stow's manual would need updating to describe how
to install these two modules (and any dependencies they have now
or in the future) as well.
However, Hash::Merge is much more general than stow actually
needs, so replace the merge() call with a few lines of equivalent
code -- this avoids the external dependencies, and is clearer than
the merge() call.
Many thanks to Adam Sampson for this patch!
https://lists.gnu.org/archive/html/bug-stow/2019-06/msg00001.html
*** Fix an issue with the test suite
t/cli.t was not testing with the right Perl executable, as
reported here:
https://rt.cpan.org/Ticket/Display.html?id=129944
Thanks to Slaven Rezic for spotting this and reporting it!
*** Various maintainer tweaks
Improved the release process and its documentation in various
minor ways.
* Changes in version 2.3.0
*** New features / changes in behaviour
***** New --dotfiles option
Enable special handling for "dotfiles" (files or folders whose name
begins with a period) in the package directory. If this option is
enabled, Stow will add a preprocessing step for each file or folder
whose name begins with "dot-", and replace the "dot-" prefix in the
name by a period ("."). This is useful when Stow is used to manage
collections of dotfiles, to avoid having a package directory full
of hidden files.
For example, suppose we have a package containing two files,
stow/dot-bashrc and stow/dot-emacs.d/init.el. With this option,
Stow will create symlinks from .bashrc to stow/dot-bashrc and from
.emacs.d/init.el to stow/dot-emacs.d/init.el. Any other files,
whose name does not begin with "dot-", will be processed as usual.
Thanks to Joris Vankerschaver for this feature!
***** Shell-like expansion in .stowrc files
For options within .stowrc files which describe file paths, "~" can
be included to expand to the current value of $HOME, and
environment variables can be referenced e.g. via "$FOO" or
"${FOO}". To prevent expansion, escape with a backslash.
Thanks a lot to Charles LeDoux for his diligent work on this
feature!
***** chkstow now honours the $STOW_DIR environment variable
The stow script already honoured the $STOW_DIR environment
variable. Now chkstow does too, for consistency.
***** Stow now has a non-zero exit code if option parsing failed
Thanks to Brice Waegeneire for reporting this.
*** License upgraded from GPL version 2 to version 3
Copyright and license notices were also added to the headers of
various files in accordance with GNU guidelines.
*** Documentation fixes and enhancements
***** Remove dependency on unmaintained texi2html
The dependency on the ancient and unmaintained texi2html for
building the single-page HTML version of the manual has been
removed, since it was difficult to get running on most distros
other than openSUSE.
Instead use the more modern "makeinfo --html --no-split" approach.
Rules have been kept for posterity in the Makefile for the old
approach and also an "in-between" approach based on texi2any;
however these are not triggered by default. Run
make manual-single-html-all
to compare the three versions.
***** Fixed naming of man page
The title of the generated man page was previously ending up as
something like:
IO::FILE=IO(0XA719C0)(1)
Thanks to @Corin-EU on GitHub highlighting this and proposing a
fix.
***** Convert README and INSTALL to Markdown
They are now named README.md and INSTALL.md, and render nicely
when viewed via git hosting services which support Markdown.
***** Update documentation to reflect more modern use cases
The README.md, stow(8) man page, and info manual have been updated
to de-emphasise the package management use, since these days almost
everyone prefers to use modern package managers such as rpm / dpkg
/ Nix for (system-wide) package management.
To compensate, more popular modern use cases for Stow have been
added, such as management of dotfiles and software compiled in the
user's $HOME directory.
***** Miscellaneous documentation fixes
- Various typos were fixed.
- The documentation for --verbose was updated to indicate that
verbosity levels now go up to 5.
- Erroneous glob examples in the --ignore documentation were fixed.
- The abbreviation "regex" was removed from the info manual for
consistency.
- INSTALL.md now also documents how to build directly from git.
*** Fixes for bugs, tests, and other technical debt
***** Add Docker files for convenient testing across multiple Perl versions
This is the first release which has been tested across 5 different
versions of Perl prior to release! The versions are:
perl-5.22.2
perl-5.20.3
perl-5.18.4
perl-5.16.3
perl-5.14.4
Thanks to Charles LeDoux for this!
***** Set up continuous testing via Travis CI
This means that the test suite will be automatically run on any
pull requests submitted to GitHub, as well as "make distcheck"
and "./Build distcheck".
***** Add Coveralls integration with GitHub
This means that test coverage analysis will be automatically be run
on any pull requests submitted to GitHub.
***** Miscellaneous improvements to the test suite
These include proper testing of the distinct impact of ~/.stowrc
and .stowrc in the directory from which Stow is invoked.
***** Fix for test suite on Cygwin
Thanks to Lucas Theisen for this fix!
***** aclocal.m4 was updated using aclocal 1.15.1.
***** Miscellaneous fixes to the build and distribution process
***** Improve handling of directories with unusual names
Various fixes for corner cases where directories are named "0"
or begin with a space character, or where STOW_DIR is empty.
Thanks to Cuong Manh Le for highlighting some of the issues and
proposing fixes!
* Changes in version 2.2.2
*** @VERSION@ substitution was set up for the Stow::Util module.
* Changes in version 2.2.1
Version 2.2.1 was not released since it was rejected by pause.perl.org
due to Stow::Util missing $VERSION.
*** Small improvements to documentation
***** The README has been revamped.
***** Some index points have been added to the manual.
***** Some typos were fixed.
***** @VERSION@ substitution was fixed in the stow(8) man page.
*** Fix Perl warnings
Stow no longer emits "v-string in use/require non-portable" and
"Possible precedence issue with control flow operator" warnings
with newer Perl versions. See https://savannah.gnu.org/bugs/?36478
and http://lists.gnu.org/archive/html/bug-stow/2014-06/msg00000.html
for full details.
*** Fix "Undefined subroutine &main::error" error
See https://rt.cpan.org/Public/Bug/Display.html?id=75349 for details.
*** Failed system calls now include error description
This should make errors easier to understand.
*** Default ignore list now ignores top-level README.*, LICENSE.*, and COPYING
These files are by definition specific to a given package, so if
they exist in the top-level directory, they should not be stowed.
*** Correctly handle the stow/target directories as non-canonical paths
Fix the case discovered by Hiroyuki Iwatsuki where stowing fails if
the stow / target directories are non-canonical paths. For
example, on FreeBSD /home is a symlink pointing to 'usr/home', so
running with the stow directory as /home/user/local/stow and the
target directory as /home/user/local previously resulted in the
stow directory path being calculated as
../../../usr/home/user/local/stow relative to the target.
See https://lists.gnu.org/archive/html/bug-stow/2013-04/msg00000.html for details.
*** Fix stowing of relative links when --no-folding is used.
With a tree like this:
.
|-- stow
| `-- pkg
| `-- lib
| |-- itk-current -> itk4.0.0
| `-- itk4.0.0
| `-- libitk4.0.0.so
`-- target
`-- lib
|-- itk4.0.0 -> ../../stow/pkg/lib/itk4.0.0
`-- libfoo-1.2.3.so
stowing pkg with the --no-folding option resulted in itk-current
being "unpacked":
.
`-- target
`-- lib
|-- itk-current
| `-- libitk4.0.0.so -> ../../../stow/pkg/lib/itk-current/libitk4.0.0.so
|-- itk4.0.0
| `-- libitk4.0.0.so -> ../../../stow/pkg/lib/itk4.0.0/libitk4.0.0.so
`-- libfoo-1.2.3.so
This commit fixes it so that it gets stowed as a symlink:
.
`-- target
`-- lib
...
|-- itk-current -> ../../stow/pkg/lib/itk-current
...
Thanks to Gabriele Balducci for reporting this problem:
https://lists.gnu.org/archive/html/help-stow/2014-09/msg00000.html
*** Internal code cleanups
***** aclocal was updated.
***** automake files were removed.
***** Trailing whitespace was removed.
***** Comments were added.
***** Debug messages were improved.
* Changes in version 2.2.0
*** New --no-folding option
Disables folding of newly stowed directories when stowing, and
refolding of newly foldable directories when unstowing.
*** Remove -a option (--adopt still available)
As --adopt is the only option which allows stow to modify files, it
is considered potentially dangerous (especially for stow package
directories which are not managed by a version control system).
Therefore it seems prudent to require a bit more effort from the
user to enable this option, minimising the change of enabling it
via a typo.
*** Improve error message when stow package is not found.
The error message displayed a path to the missing stow package
which was relative to the target directory rather than the cwd,
which was confusing for the user.
*** Test suite improvements
The test suite has been tightened up slightly.
*** Documentation improvements
Various fixes and cosmetic improvements have been made in the manual.
*** Remove "There are no outstanding operations to perform" warning.
* Changes in version 2.1.3
*** New --adopt / -a option
This allows plain files in the target to be "adopted" into the
package being stowed. See the manual has more details.
*** ./configure now checks for Perl modules required by the test suite.
* Changes in version 2.1.2
Many thanks to Stefano Lattarini for help with numerous autoconf and
automake issues which are addressed in this release.
*** Significantly improve the handling of --with-pmdir.
***** Calculation of the default value for --with-pmdir is now done safely in Perl.
Previously non-POSIX-compliant shells could cause issues.
***** The output of ./configure and make are now much more helpful.
***** The Makefile will now check whether pmdir is in Perl's built-in @INC.
If not, it will insert a
use lib "...";
line into the generated stow script to ensure that it can always
locate the Perl modules without needing to manually set PERL5LIB.
***** Updated INSTALL and HOWTO-RELEASE accordingly.
*** ./configure now aborts if Perl isn't found.
*** Ensured the ChangeLog is up-to-date when making a new distribution.
*** Fixed bug with `make clean' removing files which the user may not be able to rebuild.
* Changes in version 2.1.1
*** Fixed bug where ./configure --with-pmdir=X was ineffectual.
*** Calculated the correct default value for pmdir based on the local Perl installation.
*** Fixed some automake issues (thanks to Stefano Lattarini for spotting these!)
*** Improved various bits of documentation.
* Changes in version 2.1.0
*** Major refactoring of code into separate Stow and Stow::Util Perl modules.
*** Added support for ignore list files.
*** Added support for CPAN-style installation and distribution via Module::Build.
*** Introduced `make test' target and significantly tightened up test suite.
*** Very large number of code and documentation fixes (over 80 commits since version 2.0.1).
*** The '--conflicts' option has been removed.
Stow will always show conflicts if they are found during the scanning
phase.
*** Improved debugging output.
*** Converted man page to POD format.
*** Include PDF, and both split- and single-page HTML versions of manual in the distribution.
*** Fixed code style consistency issues.
*** Running configure from outside the source tree now works.
*** `make distcheck' now works.
* Changes in version 2.0.1
*** Defer operations until all potential conflicts have been assessed.
We do this by traversing the installation image(s) and recording the
actions that need to be performed. Redundant actions are factored out,
e.g., we don't want to create a link that we will later remove in order to
create a directory. Benefits of this approach:
1. Get to see _all_ the conflicts that are blocking an installation:
you don't have to deal with them one at a time.
2. No operations are be performed if _any_ conflicts are detected:
a failed stow will not leave you with a partially installed
package.
3. Minimises the set of operations that need to be performed.
4. Operations are executed as a batch which is much faster
This can be an advantage when upgrading packages on a live system
where you want to minimise the amount of time when the package is
unavailable.
*** The above fixes the false conflict problem mentioned in the info file.
*** It also fixes the two bugs mentioned in the man page.
*** Multiple stow directories will now cooperate in folding/unfolding.
*** Conflict messages are more uniform and informative.
*** Verbosity and tracing is more extensive and uniform.
*** Implemented option parsing via Getopt::Long.
*** Default command line arguments set via '.stowrc' and '~/.stowrc' files.
Contents of these files are parsed as though they occurred first on
the command line.
*** Support multiple actions per invocation.
In order for this to work, we had to add a new (optional) command line arg
(-S) to specify packages to stow. For example, to update an installation
of emacs you can now do
stow -D emacs-21.3 -S emacs-21.4a
which will replace emacs-21.3 with emacs-21.4a.
You can mix and match any number of actions, e.g.,
stow -S p1 p2 -D p3 p4 -S p5 -R p6
will unstow p3, p4 and p6, then stow p1, p2, p5 and p6.
*** New (repeatable) command line arg: --ignore='<regex>'
This suppresses operating on a file matching the regex (suffix),
e.g.
--ignore='~' --ignore='\.#.*'
will ignore emacs and CVS backup files (suitable for ~/.stowrc file).
(I opted for Perl regular expressions because they are more
powerful and easier to implement).
*** New (repeatable) command line arg: --defer='<regex>'
This defers stowing a file matching the regex (prefix) if that file
is already stowed to a different package, e.g.,
--defer='man' --defer='info'
will cause stow to skip over pre-existing man and info pages.
Equivalently, you could use --defer='man|info' since the argument
is just a Perl regex.
*** New (repeatable) command line arg: --override='<regex>'
This forces a file matching the regex (prefix) to be stowed even if
the file is already stowed to a different package, e.g.
--override='man' --override='info'
will unstow any pre-existing man and info pages that would conflict
with the file we are trying to stow.
Equivalently, you could use --override='man|info' since the
argument is just a Perl regex.
*** The above gives the ability to manage packages with common content.
For example, man pages that are shared by a number of CPAN
packages. Using multiple stow directories and .stowrc files can
also simplify things. In our setup we use the standard
/usr/local/stow directory for packages to be installed in
/usr/local. Since we install a large number of extra Perl packages
(currently about 300) we use an additional stow directory:
/usr/local/stow/perl-5.8.8-extras. Both stow directories contain a
'.stow' file so that they collaborate appropriately. I then use
the following .stowrc file in /usr/local/stow/perl-5.8.8-extras
--dir=/usr/local/stow/perl-5.8.8-extras
--target=/usr/local
--override=bin
--override=man
--ignore='perllocal\.pod'
--ignore='\.packlist'
--ignore='\.bs'
When I stow packages from there, they automatically override any
man pages and binaries that may already have been stowed by another
package or by the core perl-5.8.8 installation. For example, if
you want to upgrade the Test-Simple package, you need to override
all the man pages that would have been installed by the core
package. If you are upgrading CPAN, you will also have to override
the pre-existing cpan executable.
*** By default, search less aggressively for invalid symlinks when unstowing.
That is, we only search for bad symlinks in the directories
explicitly mentioned in the installation image, and do not dig down
into other subdirs. Digging down into other directories can be
very time consuming if you have a really big tree (like with a
couple of Oracle installations lying around). In general the old
behaviour is only necessary when you have really stuffed up your
installation by deleting a directory that has already been stowed.
Doing that on a live system is somewhat crazy and hopefully rare.
We provide an option '-p|--compat' to enable the old behaviour for
those needing to patch up mistakes.
*** New chkstow utility for checking the integrity of the target directory.
*** Implement a test suite and support code.
This was built before implementing any of the extra features so I
could more easily check for equivalent functionality. The initial
code base had to be refactored substantially to allow for testing.
The test suite is not exhaustive, but it should provide enough to
check for regressions.
* Changes in version 1.3.3
*** Now requires Perl 5.005 or later
*** Initially empty directories are not removed anymore
*** Removed buggy fastcwd (we use POSIX::getcwd instead)
*** Fixed bug when the common Parent of Target dir and Stow dir was "/"
*** Fixed bug when handling directories named "0"
*** Stow now only warns the user if a directory is unreadable during unstowing.
* Changes in version 1.3:
*** Added --restow option.
*** Fixed handling of slashes in package names.
*** Expanded configure-time search for Perl binary.
* Changes in version 1.2:
*** Dependency on `pwd' removed.
*** Perl 4 compatibility fixes.
*** Manual expanded even more.
* Changes in version 1.1:
*** Long and short options now accepted.
*** Manual expanded.
*** `make clean' removes stow (which is generated from stow.in).
* Initial public release (v1.0) of Stow.
* emacs local variables
Local Variables:
mode: org
org-export-with-toc: nil
org-export-with-author: nil
org-toc-odd-levels-only: t
org-blank-before-new-entry: ((heading . auto) (plain-list-item . auto))
End:

28
README
View file

@ -1,28 +0,0 @@
This is GNU Stow, a program for managing the installation of software
packages, keeping them separate (/usr/local/stow/emacs
vs. /usr/local/stow/perl, for example) while making them appear to be
installed in the same place (/usr/local).
Stow is a Perl script which should run correctly under Perl 4 and Perl
5. You must install Perl before running Stow. For more information
about Perl, see http://www.perl.com/perl/.
You can get the latest information about Stow from
http://www.gnu.ai.mit.edu/software/stow/stow.html.
Stow was inspired by Carnegie Mellon's "Depot" program, but is
substantially simpler. Whereas Depot requires database files to keep
things in sync, Stow stores no extra state between runs, so there's no
danger (as there is in Depot) of mangling directories when file
hierarchies don't match the database. Also unlike Depot, Stow will
never delete any files, directories, or links that appear in a Stow
directory (e.g., /usr/local/stow/emacs), so it's always possible to
rebuild the target tree (e.g., /usr/local).
Stow is free software, licensed under the GNU General Public License,
which can be found in the file COPYING.
See INSTALL for installation instructions.
Please mail comments, questions, and criticisms to the author, Bob
Glickstein, <bobg+stow@zanshin.com>.

139
README.md Normal file
View file

@ -0,0 +1,139 @@
[![Build Status](https://travis-ci.org/aspiers/stow.svg)](https://travis-ci.org/aspiers/stow)
[![Coverage Status](https://coveralls.io/repos/aspiers/stow/badge.svg?branch=master&service=github)](https://coveralls.io/github/aspiers/stow?branch=master)
README for GNU Stow
===================
This README describes GNU Stow. This is not the definitive
documentation for Stow; for that, see the [info
manual](https://www.gnu.org/software/stow/manual/).
Stow is a symlink farm manager program which takes distinct sets
of software and/or data located in separate directories on the
filesystem, and makes them all appear to be installed in a single
directory tree.
Originally Stow was born to address the need to administer, upgrade,
install, and remove files in independent software packages without
confusing them with other files sharing the same file system space.
For instance, many years ago it used to be common to compile programs
such as Perl and Emacs from source and install them in `/usr/local`.
By using Stow, `/usr/local/bin` could contain symlinks to files within
`/usr/local/stow/emacs/bin`, `/usr/local/stow/perl/bin` etc., and
likewise recursively for any other subdirectories such as `.../share`,
`.../man`, and so on.
While this is useful for keeping track of system-wide and per-user
installations of software built from source, in more recent times
software packages are often managed by more sophisticated package
management software such as
[`rpm`](https://en.wikipedia.org/wiki/Rpm_(software)),
[`dpkg`](https://en.wikipedia.org/wiki/Dpkg), and
[Nix](https://en.wikipedia.org/wiki/Nix_package_manager) / [GNU
Guix](https://en.wikipedia.org/wiki/GNU_Guix), or language-native
package managers such as Ruby's
[`gem`](https://en.wikipedia.org/wiki/RubyGems), Python's
[`pip`](https://en.wikipedia.org/wiki/Pip_(package_manager)),
Javascript's [`npm`](https://en.wikipedia.org/wiki/Npm_(software)),
and so on.
However Stow is still used not only for software package management,
but also for other purposes, such as facilitating [a more controlled
approach to management of configuration files in the user's home
directory](http://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html),
especially when [coupled with version control
systems](http://lists.gnu.org/archive/html/info-stow/2011-12/msg00000.html).
Stow was inspired by Carnegie Mellon's Depot program, but is
substantially simpler and safer. Whereas Depot required database files
to keep things in sync, Stow stores no extra state between runs, so
there's no danger (as there was in Depot) of mangling directories when
file hierarchies don't match the database. Also unlike Depot, Stow will
never delete any files, directories, or links that appear in a Stow
directory (e.g., `/usr/local/stow/emacs`), so it's always possible
to rebuild the target tree (e.g., `/usr/local`).
Stow is implemented as a combination of a Perl script providing a CLI
interface, and a backend Perl module which does most of the work.
You can get the latest information about Stow from the home page:
http://www.gnu.org/software/stow/
Installation
------------
See [`INSTALL.md`](INSTALL.md) for installation instructions.
Documentation
-------------
Documentation for Stow is available
[online](https://www.gnu.org/software/stow/manual/), as is
[documentation for most GNU
software](https://www.gnu.org/software/manual/). Once you have Stow
installed, you may also find more information about Stow by running
`info stow` or `man stow`, or by looking at `/usr/share/doc/stow/`,
`/usr/local/doc/stow/`, or similar directories on your system. A
brief summary is available by running `stow --help`.
Mailing lists
-------------
Stow has the following mailing lists:
- [help-stow](https://lists.gnu.org/mailman/listinfo/help-stow) is for
general user help and discussion.
- [stow-devel](https://lists.gnu.org/mailman/listinfo/stow-devel) is
used to discuss most aspects of Stow, including development and
enhancement requests.
- [bug-stow](https://lists.gnu.org/mailman/listinfo/bug-stow) is for
bug reports.
Announcements about Stow are posted to
[info-stow](http://lists.gnu.org/mailman/listinfo/info-stow) and also,
as with most other GNU software, to
[info-gnu](http://lists.gnu.org/mailman/listinfo/info-gnu)
([archive](http://lists.gnu.org/archive/html/info-gnu/)).
Security reports that should not be made immediately public can be
sent directly to the maintainer. If there is no response to an urgent
issue, you can escalate to the general
[security](http://lists.gnu.org/mailman/listinfo/security) mailing
list for advice.
The Savannah project also has a [mailing
lists](https://savannah.gnu.org/mail/?group=stow) page.
Getting involved
----------------
Please see the [`CONTRIBUTING.md` file](CONTRIBUTING.md).
License
-------
Stow is free software, licensed under the GNU General Public License,
which can be found in the file [`COPYING`](COPYING).
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.
Brief history and authorship
----------------------------
Stow was inspired by Carnegie Mellon's "Depot" program, but is
substantially simpler. Whereas Depot requires database files to keep
things in sync, Stow stores no extra state between runs, so there's no
danger (as there is in Depot) of mangling directories when file
hierarchies don't match the database. Also unlike Depot, Stow will
never delete any files, directories, or links that appear in a Stow
directory (e.g., `/usr/local/stow/emacs`), so it's always possible to
rebuild the target tree (e.g., `/usr/local`).
For a high-level overview of the contributions of the main developers
over the years, see [the `AUTHORS` file](AUTHORS).
For a more detailed history, please see the `ChangeLog` file.

57
THANKS
View file

@ -1,16 +1,47 @@
Thanks to the following people for testing, using, commenting on, and
otherwise aiding the creation of Stow:
Miles Bader <miles@gnu.ai.mit.edu>
Greg Fox <fox@zanshin.com>
David Hartmann <davidh@zanshin.com>
Ben Liblit <liblit@well.com>
Gord Matzigkeit <gord@enci.ucalgary.ca>
Roland McGrath <roland@gnu.ai.mit.edu>
Jim Meyering <meyering@asic.sc.ti.com>
Fritz Mueller <fritzm@netcom.com>
Bart Schaefer <schaefer@nbn.com>
Richard Stallman <rms@gnu.ai.mit.edu>
Spencer Sun <zorak@netcom.com>
Tom Tromey <tromey@cygnus.com>
Steve Webster <srw@zanshin.com>
Bob Glickstein (original author)
Miles Bader <miles@gnu.ai.mit.edu>
Greg Fox <fox@zanshin.com>
David Hartmann <davidh@zanshin.com>
Ben Liblit <liblit@well.com>
Gord Matzigkeit <gord@enci.ucalgary.ca>
Adam Lackorzynski <al10@inf.tu-dresden.de>
Roland McGrath <roland@gnu.ai.mit.edu>
Jim Meyering <meyering@asic.sc.ti.com>
Fritz Mueller <fritzm@netcom.com>
Bart Schaefer <schaefer@nbn.com>
Richard Stallman <rms@gnu.ai.mit.edu>
Spencer Sun <zorak@netcom.com>
Tom Tromey <tromey@cygnus.com>
Steve Webster <srw@zanshin.com>
Kahlil Hodgson <kahlil@internode.on.net>
Geoffrey Giesemann <geoffrey.giesemann@rmit.edu.au>
Emil Mikulic <emil.mikulic@rmit.edu.au>
Austin Wood <austin.wood@rmit.edu.au>
Christopher Hoobin <christopher.hoobin.edu.au>
Adam Spiers <stow@adamspiers.org>
Troy Will
Stefano Lattarini
Adam Sampson
Cuong Manh Le
Lucas Theisen
Charles LeDoux
Joris Vankerschaver
@Corin-EU on GitHub
Kristoffer Haugsbakk
Hongyi Zhao
Jean Louis
Daniel Shahaf
Matan Nassau
Brice Waegeneire
Slaven Rezic
Email addresses of new contributors are no longer being added by default
for privacy reasons; however please contact the maintainer if you are
happy for your email address to be listed here.
More authorship and contribution details can be found in the AUTHORS
and ChangeLog files, and of course also in the git version control
history.

69
TODO
View file

@ -1,16 +1,45 @@
-*- outline -*-
* Add support for pre/post-(un)install hooks
This would allow correct handling of the Info dir file via
install-info, amongst other things:
*** http://unix.stackexchange.com/questions/73426/dealing-with-gnu-stow-conflicts
*** https://lists.gnu.org/archive/html/help-stow/2013-04/msg00016.html
* Get permission for next documentation release to be under FDL 1.3
* Import a debian/ tree from an older package and update it.
* Import a .spec file from somewhere and update it.
* Distinguish between .stow and (undocumented) .nonstow / .notstowed
** .stow is for marking stow directories - avoids altering them
but also allows --override to work
** .nonstow should be only for protecting non-stow directories against modification by stow
but currently allows modification via --override
** .notstowed is only honoured by chkstow
** Documentation needs to be clear on this.
* Prevent folding of directories which contain ignored files
* Honour .no-stow-folding and --no-folding
* Add semi-automatic conflict resolution
(This idea is possibly obsoleted via --override and --adopt.)
*** STOW_RESOLVE_CONFLICTS="non_stow_symlinks=t stow_symlinks=r"
*** Add documentation about conflict resolution
* Autodetect "foreign" stow directories
* Fix empty-dir problem (see "Known bugs" in the manual)
* Continue after conflicts.
When detecting a conflict, affected subparts of the Stow traversal can
be skipped while continuing with other subparts.
* Traverse links in the target tree?
From e-mail with meyering@na-net.ornl.gov:
> My /usr/local/info equivalent is a symlink to /share/info
@ -32,16 +61,12 @@ From e-mail with meyering@na-net.ornl.gov:
should it be an enumeration of which links are OK to traverse
(such as, "--traversable='info man doc'")?
* Develop a mechanism for sharing files between packages.
Does Version 2 fix this? (Kal)
I think that because it never needs to create /usr/local/info,
it only needs to check the ownership of links that it _operates_ on,
not on all the elements of the path.
This would solve the problem of maintaining N platform-specific copies
of a package, all of which have many platform-*independent* files
which could be shared, such as man pages, info files, etc.
* Option to ignore certain files in the stow tree.
For example, --ignore='*~ .#*' (skip Emacs and CVS backup files).
* Option to ignore links in the stow tree to certain places.
For example, --ignore-link='/*' (skip absolute links).
* emacs local variables
Local Variables:
mode: org
End:

756
aclocal.m4 vendored
View file

@ -1,7 +1,753 @@
dnl This definition comes from automake 1.0
# generated automatically by aclocal 1.16.5 -*- Autoconf -*-
AC_DEFUN(fp_PROG_INSTALL,
[AC_PROG_INSTALL
test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL} -m 755'
AC_SUBST(INSTALL_SCRIPT)dnl
# Copyright (C) 1996-2021 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.72],,
[m4_warning([this file was generated for autoconf 2.72.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
# Copyright (C) 2002-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.16'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.16.5], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.16.5])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is '.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
# Expand $ac_aux_dir to an absolute path.
am_aux_dir=`cd "$ac_aux_dir" && pwd`
])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])
[_AM_PROG_CC_C_O
])
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.65])dnl
m4_ifdef([_$0_ALREADY_INIT],
[m4_fatal([$0 expanded multiple times
]m4_defn([_$0_ALREADY_INIT]))],
[m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[AC_DIAGNOSE([obsolete],
[$0: two- and three-arguments forms are deprecated.])
m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(
m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]),
[ok:ok],,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
AM_MISSING_PROG([AUTOCONF], [autoconf])
AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
AM_MISSING_PROG([AUTOHEADER], [autoheader])
AM_MISSING_PROG([MAKEINFO], [makeinfo])
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
# We need awk for the "check" target (and possibly the TAP driver). The
# system "awk" is bad on some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES([CC])],
[m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES([CXX])],
[m4_define([AC_PROG_CXX],
m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES([OBJC])],
[m4_define([AC_PROG_OBJC],
m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
[_AM_DEPENDENCIES([OBJCXX])],
[m4_define([AC_PROG_OBJCXX],
m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
])
# Variables for tags utilities; see am/tags.am
if test -z "$CTAGS"; then
CTAGS=ctags
fi
AC_SUBST([CTAGS])
if test -z "$ETAGS"; then
ETAGS=etags
fi
AC_SUBST([ETAGS])
if test -z "$CSCOPE"; then
CSCOPE=cscope
fi
AC_SUBST([CSCOPE])
AC_REQUIRE([AM_SILENT_RULES])dnl
dnl The testsuite driver may need to know about EXEEXT, so add the
dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
# POSIX will say in a future version that running "rm -f" with no argument
# is OK; and we want to be able to make that assumption in our Makefile
# recipes. So use an aggressive probe to check that the usage we want is
# actually supported "in the wild" to an acceptable degree.
# See automake bug#10828.
# To make any issue more visible, cause the running configure to be aborted
# by default if the 'rm' program in use doesn't match our expectations; the
# user can still override this though.
if rm -f && rm -fr && rm -rf; then : OK; else
cat >&2 <<'END'
Oops!
Your 'rm' program seems unable to run without file operands specified
on the command line, even when the '-f' option is present. This is contrary
to the behaviour of most rm programs out there, and not conforming with
the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
Please tell bug-automake@gnu.org about your system, including the value
of your $PATH and any error possibly output before this message. This
can help us improve future automake versions.
END
if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
echo 'Configuration will proceed anyway, since you have set the' >&2
echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
echo >&2
else
cat >&2 <<'END'
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
that behaves properly: <https://www.gnu.org/software/coreutils/>.
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
to "yes", and re-run configure.
END
AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
fi
fi
dnl The trailing newline in this macro's definition is deliberate, for
dnl backward compatibility and to allow trailing 'dnl'-style comments
dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
])
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_arg=$1
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$_am_arg | $_am_arg:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
*)
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
AC_SUBST([install_sh])])
# Copyright (C) 2003-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it is modern enough.
# If it is, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
MISSING="\${SHELL} '$am_aux_dir/missing'"
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
am_missing_run="$MISSING "
else
am_missing_run=
AC_MSG_WARN(['missing' script is too old or missing])
fi
])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# --------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
# _AM_SET_OPTIONS(OPTIONS)
# ------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[[\\\"\#\$\&\'\`$am_lf]]*)
AC_MSG_ERROR([unsafe absolute working directory name]);;
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
esac
# Do 'set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
am_has_slept=no
for am_try in 1 2; do
echo "timestamp, slept: $am_has_slept" > conftest.file
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
if test "$[*]" = "X"; then
# -L didn't work.
set X `ls -t "$srcdir/configure" conftest.file`
fi
if test "$[*]" != "X $srcdir/configure conftest.file" \
&& test "$[*]" != "X conftest.file $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
if test "$[2]" = conftest.file || test $am_try -eq 2; then
break
fi
# Just in case.
sleep 1
am_has_slept=yes
done
test "$[2]" = conftest.file
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
AC_MSG_RESULT([yes])
# If we didn't sleep, we still need to ensure time stamps of config.status and
# generated files are strictly newer.
am_sleep_pid=
if grep 'slept: no' conftest.file >/dev/null 2>&1; then
( sleep 1 ) &
am_sleep_pid=$!
fi
AC_CONFIG_COMMANDS_PRE(
[AC_MSG_CHECKING([that generated files are newer than configure])
if test -n "$am_sleep_pid"; then
# Hide warnings about reused PIDs.
wait $am_sleep_pid 2>/dev/null
fi
AC_MSG_RESULT([done])])
rm -f conftest.file
])
# Copyright (C) 2009-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_SILENT_RULES([DEFAULT])
# --------------------------
# Enable less verbose build rules; with the default set to DEFAULT
# ("yes" being less verbose, "no" or empty being verbose).
AC_DEFUN([AM_SILENT_RULES],
[AC_ARG_ENABLE([silent-rules], [dnl
AS_HELP_STRING(
[--enable-silent-rules],
[less verbose build output (undo: "make V=1")])
AS_HELP_STRING(
[--disable-silent-rules],
[verbose build output (undo: "make V=0")])dnl
])
case $enable_silent_rules in @%:@ (((
yes) AM_DEFAULT_VERBOSITY=0;;
no) AM_DEFAULT_VERBOSITY=1;;
*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
esac
dnl
dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
dnl do not support nested variable expansions.
dnl See automake bug#9928 and bug#10237.
am_make=${MAKE-make}
AC_CACHE_CHECK([whether $am_make supports nested variables],
[am_cv_make_support_nested_variables],
[if AS_ECHO([['TRUE=$(BAR$(V))
BAR0=false
BAR1=true
V=1
am__doit:
@$(TRUE)
.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
am_cv_make_support_nested_variables=yes
else
am_cv_make_support_nested_variables=no
fi])
if test $am_cv_make_support_nested_variables = yes; then
dnl Using '$V' instead of '$(V)' breaks IRIX make.
AM_V='$(V)'
AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
else
AM_V=$AM_DEFAULT_VERBOSITY
AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
fi
AC_SUBST([AM_V])dnl
AM_SUBST_NOTMAKE([AM_V])dnl
AC_SUBST([AM_DEFAULT_V])dnl
AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
AM_BACKSLASH='\'
AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
# Copyright (C) 2001-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor 'install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in "make install-strip", and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using 'strip' when the user
# run "make install-strip". However 'strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the 'STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004-2021 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of 'v7', 'ustar', or 'pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
#
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AC_SUBST([AMTAR], ['$${TAR-tar}'])
# We'll loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
m4_if([$1], [v7],
[am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
[m4_case([$1],
[ustar],
[# The POSIX 1988 'ustar' format is defined with fixed-size fields.
# There is notably a 21 bits limit for the UID and the GID. In fact,
# the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
# and bug#13588).
am_max_uid=2097151 # 2^21 - 1
am_max_gid=$am_max_uid
# The $UID and $GID variables are not portable, so we need to resort
# to the POSIX-mandated id(1) utility. Errors in the 'id' calls
# below are definitely unexpected, so allow the users to see them
# (that is, avoid stderr redirection).
am_uid=`id -u || echo unknown`
am_gid=`id -g || echo unknown`
AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
if test $am_uid -le $am_max_uid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi
AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
if test $am_gid -le $am_max_gid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi],
[pax],
[],
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Go ahead even if we have the value already cached. We do so because we
# need to set the values for the 'am__tar' and 'am__untar' variables.
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
for _am_tool in $_am_tools; do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar; do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works.
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar <conftest.tar])
AM_RUN_LOG([cat conftest.dir/file])
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR

5
automake/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
install-sh
missing
mdate-sh
test-driver
texinfo.tex

127
bin/chkstow.in Executable file
View file

@ -0,0 +1,127 @@
#!@PERL@
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
use strict;
use warnings;
require 5.006_001;
use File::Find;
use Getopt::Long;
my $DEFAULT_TARGET = $ENV{STOW_DIR} || '/usr/local/';
our $Wanted = \&bad_links;
our %Package = ();
our $Stow_dir = '';
our $Target = $DEFAULT_TARGET;
# put the main loop into a block so that tests can load this as a module
if ( not caller() ) {
if (@ARGV == 0) {
usage();
}
process_options();
#check_stow($Target, $Wanted);
check_stow();
}
sub process_options {
GetOptions(
'b|badlinks' => sub { $Wanted = \&bad_links },
'a|aliens' => sub { $Wanted = \&aliens },
'l|list' => sub { $Wanted = \&list },
't|target=s' => \$Target,
) or usage();
return;
}
sub usage {
print <<"EOT";
USAGE: chkstow [options]
Options:
-t DIR, --target=DIR Set the target directory to DIR
(default is $DEFAULT_TARGET)
-b, --badlinks Report symlinks that point to non-existent files
-a, --aliens Report non-symlinks in the target directory
-l, --list List packages in the target directory
--badlinks is the default mode.
EOT
exit(0);
}
sub check_stow {
#my ($Target, $Wanted) = @_;
my (%options) = (
wanted => $Wanted,
preprocess => \&skip_dirs,
);
find(\%options, $Target);
if ($Wanted == \&list) {
delete $Package{''};
delete $Package{'..'};
if (keys %Package) {
print map "$_\n", sort(keys %Package);
}
}
return;
}
sub skip_dirs {
# skip stow source and unstowed targets
if (-e ".stow" || -e ".notstowed" ) {
warn "skipping $File::Find::dir\n";
return ();
}
else {
return @_;
}
}
# checking for files that do not link to anything
sub bad_links {
-l && !-e && print "Bogus link: $File::Find::name\n";
}
# checking for files that are not owned by stow
sub aliens {
!-l && !-d && print "Unstowed file: $File::Find::name\n";
}
# just list the packages in the target directory
# FIXME: what if the stow dir is not called 'stow'?
sub list {
if (-l) {
$_ = readlink;
s{\A(?:\.\./)+stow/}{}g;
s{/.*}{}g;
$Package{$_} = 1;
}
}
1; # Hey, it's a module!
# Local variables:
# mode: perl
# End:
# vim: ft=perl

852
bin/stow.in Executable file
View file

@ -0,0 +1,852 @@
#!@PERL@
# GNU Stow - manage farms of symbolic links
# Copyright (C) 1993, 1994, 1995, 1996 by Bob Glickstein
# Copyright (C) 2000, 2001 Guillaume Morin
# Copyright (C) 2007 Kahlil Hodgson
# Copyright (C) 2011 Adam Spiers
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
=head1 NAME
stow - manage farms of symbolic links
=head1 SYNOPSIS
stow [ options ] package ...
=head1 DESCRIPTION
This manual page describes GNU Stow @VERSION@. This is not the
definitive documentation for Stow; for that, see the accompanying info
manual, e.g. by typing C<info stow>.
Stow is a symlink farm manager which takes distinct sets of software
and/or data located in separate directories on the filesystem, and
makes them all appear to be installed in a single directory tree.
Originally Stow was born to address the need to administer, upgrade,
install, and remove files in independent software packages without
confusing them with other files sharing the same file system space.
For instance, many years ago it used to be common to compile programs
such as Perl and Emacs from source. By using Stow, F</usr/local/bin>
could contain symlinks to files within F</usr/local/stow/emacs/bin>,
F</usr/local/stow/perl/bin> etc., and likewise recursively for any
other subdirectories such as F<.../share>, F<.../man>, and so on.
While this is useful for keeping track of system-wide and per-user
installations of software built from source, in more recent times
software packages are often managed by more sophisticated package
management software such as rpm, dpkg, and Nix / GNU Guix, or
language-native package managers such as Ruby's gem, Python's pip,
Javascript's npm, and so on.
However Stow is still used not only for software package management,
but also for other purposes, such as facilitating a more controlled
approach to management of configuration files in the user's home
directory, especially when coupled with version control systems.
Stow was inspired by Carnegie Mellon's Depot program, but is
substantially simpler and safer. Whereas Depot required database files
to keep things in sync, Stow stores no extra state between runs, so
there's no danger (as there was in Depot) of mangling directories when
file hierarchies don't match the database. Also unlike Depot, Stow
will never delete any files, directories, or links that appear in a
Stow directory (e.g., F</usr/local/stow/emacs>), so it's always
possible to rebuild the target tree (e.g., F</usr/local>).
Stow is implemented as a combination of a Perl script providing a CLI
interface, and a backend Perl module which does most of the work.
=head1 TERMINOLOGY
A "package" is a related collection of files and directories that
you wish to administer as a unit -- e.g., Perl or Emacs -- and that
needs to be installed in a particular directory structure -- e.g.,
with F<bin>, F<lib>, and F<man> subdirectories.
A "target directory" is the root of a tree in which one or more
packages wish to B<appear> to be installed. A common, but by no means
the only such location is F</usr/local>. The examples in this manual
page will use F</usr/local> as the target directory.
A "stow directory" is the root of a tree containing separate
packages in private subtrees. When Stow runs, it uses the current
directory as the default stow directory. The examples in this manual
page will use F</usr/local/stow> as the stow directory, so that
individual packages will be, for example, F</usr/local/stow/perl> and
F</usr/local/stow/emacs>.
An "installation image" is the layout of files and directories
required by a package, relative to the target directory. Thus, the
installation image for Perl includes: a F<bin> directory containing
F<perl> and F<a2p> (among others); an F<info> directory containing
Texinfo documentation; a F<lib/perl> directory containing Perl
libraries; and a F<man/man1> directory containing man pages.
A "package directory" is the root of a tree containing the
installation image for a particular package. Each package directory
must reside in a stow directory -- e.g., the package directory
F</usr/local/stow/perl> must reside in the stow directory
F</usr/local/stow>. The "name" of a package is the name of its
directory within the stow directory -- e.g., F<perl>.
Thus, the Perl executable might reside in
F</usr/local/stow/perl/bin/perl>, where F</usr/local> is the target
directory, F</usr/local/stow> is the stow directory,
F</usr/local/stow/perl> is the package directory, and F<bin/perl>
within is part of the installation image.
A "symlink" is a symbolic link. A symlink can be "relative" or
"absolute". An absolute symlink names a full path; that is, one
starting from F</>. A relative symlink names a relative path; that
is, one not starting from F</>. The target of a relative symlink is
computed starting from the symlink's own directory. Stow only creates
relative symlinks.
=head1 OPTIONS
The stow directory is assumed to be the value of the C<STOW_DIR>
environment variable or if unset the current directory, and the target
directory is assumed to be the parent of the current directory (so it
is typical to execute F<stow> from the directory F</usr/local/stow>).
Each F<package> given on the command line is the name of a package in
the stow directory (e.g., F<perl>). By default, they are installed
into the target directory (but they can be deleted instead using
C<-D>).
=over 4
=item -n
=item --no
=item --simulate
Do not perform any operations that modify the filesystem; merely show
what would happen.
=item -d DIR
=item --dir=DIR
Set the stow directory to C<DIR> instead of the current directory.
This also has the effect of making the default target directory be the
parent of C<DIR>.
=item -t DIR
=item --target=DIR
Set the target directory to C<DIR> instead of the parent of the stow
directory.
=item -v
=item --verbose[=N]
Send verbose output to standard error describing what Stow is
doing. Verbosity levels are from 0 to 5; 0 is the default.
Using C<-v> or C<--verbose> increases the verbosity by one; using
`--verbose=N' sets it to N.
=item -S
=item --stow
Stow the packages that follow this option into the target directory.
This is the default action and so can be omitted if you are only
stowing packages rather than performing a mixture of
stow/delete/restow actions.
=item -D
=item --delete
Unstow the packages that follow this option from the target directory rather
than installing them.
=item -R
=item --restow
Restow packages (first unstow, then stow again). This is useful
for pruning obsolete symlinks from the target tree after updating
the software in a package.
=item --adopt
B<Warning!> This behaviour is specifically intended to alter the
contents of your stow directory. If you do not want that, this option
is not for you.
When stowing, if a target is encountered which already exists but is a
plain file (and hence not owned by any existing stow package), then
normally Stow will register this as a conflict and refuse to proceed.
This option changes that behaviour so that the file is moved to the
same relative place within the package's installation image within the
stow directory, and then stowing proceeds as before. So effectively,
the file becomes adopted by the stow package, without its contents
changing.
=item --no-folding
Disable folding of newly stowed directories when stowing, and
refolding of newly foldable directories when unstowing.
=item --ignore=REGEX
Ignore files ending in this Perl regex.
=item --defer=REGEX
Don't stow files beginning with this Perl regex if the file is already
stowed to another package.
=item --override=REGEX
Force stowing files beginning with this Perl regex if the file is
already stowed to another package.
=item --dotfiles
Enable special handling for "dotfiles" (files or folders whose name
begins with a period) in the package directory. If this option is
enabled, Stow will add a preprocessing step for each file or folder
whose name begins with "dot-", and replace the "dot-" prefix in the
name by a period (.). This is useful when Stow is used to manage
collections of dotfiles, to avoid having a package directory full of
hidden files.
For example, suppose we have a package containing two files,
F<stow/dot-bashrc> and F<stow/dot-emacs.d/init.el>. With this option,
Stow will create symlinks from F<.bashrc> to F<stow/dot-bashrc> and
from F<.emacs.d/init.el> to F<stow/dot-emacs.d/init.el>. Any other
files, whose name does not begin with "dot-", will be processed as usual.
=item -V
=item --version
Show Stow version number, and exit.
=item -h
=item --help
Show Stow command syntax, and exit.
=back
=head1 INSTALLING PACKAGES
The default action of Stow is to install a package. This means
creating symlinks in the target tree that point into the package tree.
Stow attempts to do this with as few symlinks as possible; in other
words, if Stow can create a single symlink that points to an entire
subtree within the package tree, it will choose to do that rather than
create a directory in the target tree and populate it with symlinks.
For example, suppose that no packages have yet been installed in
F</usr/local>; it's completely empty (except for the F<stow>
subdirectory, of course). Now suppose the Perl package is installed.
Recall that it includes the following directories in its installation
image: F<bin>; F<info>; F<lib/perl>; F<man/man1>. Rather than
creating the directory F</usr/local/bin> and populating it with
symlinks to F<../stow/perl/bin/perl> and F<../stow/perl/bin/a2p> (and
so on), Stow will create a single symlink, F</usr/local/bin>, which
points to F<stow/perl/bin>. In this way, it still works to refer to
F</usr/local/bin/perl> and F</usr/local/bin/a2p>, and fewer symlinks
have been created. This is called "tree folding", since an entire
subtree is "folded" into a single symlink.
To complete this example, Stow will also create the symlink
F</usr/local/info> pointing to F<stow/perl/info>; the symlink
F</usr/local/lib> pointing to F<stow/perl/lib>; and the symlink
F</usr/local/man> pointing to F<stow/perl/man>.
Now suppose that instead of installing the Perl package into an empty
target tree, the target tree is not empty to begin with. Instead, it
contains several files and directories installed under a different
system-administration philosophy. In particular, F</usr/local/bin>
already exists and is a directory, as are F</usr/local/lib> and
F</usr/local/man/man1>. In this case, Stow will descend into
F</usr/local/bin> and create symlinks to F<../stow/perl/bin/perl> and
F<../stow/perl/bin/a2p> (etc.), and it will descend into
F</usr/local/lib> and create the tree-folding symlink F<perl> pointing
to F<../stow/perl/lib/perl>, and so on. As a rule, Stow only descends
as far as necessary into the target tree when it can create a
tree-folding symlink.
The time often comes when a tree-folding symlink has to be undone
because another package uses one or more of the folded subdirectories
in its installation image. This operation is called "splitting open"
a folded tree. It involves removing the original symlink from the
target tree, creating a true directory in its place, and then
populating the new directory with symlinks to the newly-installed
package B<and> to the old package that used the old symlink. For
example, suppose that after installing Perl into an empty
F</usr/local>, we wish to install Emacs. Emacs's installation image
includes a F<bin> directory containing the F<emacs> and F<etags>
executables, among others. Stow must make these files appear to be
installed in F</usr/local/bin>, but presently F</usr/local/bin> is a
symlink to F<stow/perl/bin>. Stow therefore takes the following
steps: the symlink F</usr/local/bin> is deleted; the directory
F</usr/local/bin> is created; links are made from F</usr/local/bin> to
F<../stow/emacs/bin/emacs> and F<../stow/emacs/bin/etags>; and links
are made from F</usr/local/bin> to F<../stow/perl/bin/perl> and
F<../stow/perl/bin/a2p>.
When splitting open a folded tree, Stow makes sure that the symlink
it is about to remove points inside a valid package in the current stow
directory.
=head2 Stow will never delete anything that it doesn't own.
Stow "owns" everything living in the target tree that points into a
package in the stow directory. Anything Stow owns, it can recompute if
lost. Note that by this definition, Stow doesn't "own" anything
B<in> the stow directory or in any of the packages.
If Stow needs to create a directory or a symlink in the target tree
and it cannot because that name is already in use and is not owned by
Stow, then a conflict has arisen. See the "Conflicts" section in the
info manual.
=head1 DELETING PACKAGES
When the C<-D> option is given, the action of Stow is to delete a
package from the target tree. Note that Stow will not delete anything
it doesn't "own". Deleting a package does B<not> mean removing it from
the stow directory or discarding the package tree.
To delete a package, Stow recursively scans the target tree, skipping
over the stow directory (since that is usually a subdirectory of the
target tree) and any other stow directories it encounters (see
"Multiple stow directories" in the info manual). Any symlink it
finds that points into the package being deleted is removed. Any
directory that contained only symlinks to the package being deleted is
removed. Any directory that, after removing symlinks and empty
subdirectories, contains only symlinks to a single other package, is
considered to be a previously "folded" tree that was "split open."
Stow will re-fold the tree by removing the symlinks to the surviving
package, removing the directory, then linking the directory back to
the surviving package.
=head1 RESOURCE FILES
F<Stow> searches for default command line options at F<.stowrc> (current
directory) and F<~/.stowrc> (home directory) in that order. If both
locations are present, the files are effectively appended together.
The effect of options in the resource file is similar to simply prepending
the options to the command line. For options that provide a single value,
such as F<--target> or F<--dir>, the command line option will overwrite any
options in the resource file. For options that can be given more than once,
F<--ignore> for example, command line options and resource options are
appended together.
Environment variables and the tilde character (F<~>) will be expanded for
options that take a file path.
The options F<-D>, F<-R>, F<-S>, and any packages listed in the resource
file are ignored.
See the info manual for more information on how stow handles resource
file.
=head1 SEE ALSO
The full documentation for F<stow> is maintained as a Texinfo manual.
If the F<info> and F<stow> programs are properly installed at your site, the command
info stow
should give you access to the complete manual.
=head1 BUGS
Please report bugs in Stow using the Debian bug tracking system.
Currently known bugs include:
=over 4
=item * The empty-directory problem.
If package F<foo> includes an empty directory -- say, F<foo/bar> --
then if no other package has a F<bar> subdirectory, everything's fine.
If another stowed package F<quux>, has a F<bar> subdirectory, then
when stowing, F<targetdir/bar> will be "split open" and the contents
of F<quux/bar> will be individually stowed. So far, so good. But when
unstowing F<quux>, F<targetdir/bar> will be removed, even though
F<foo/bar> needs it to remain. A workaround for this problem is to
create a file in F<foo/bar> as a placeholder. If you name that file
F<.placeholder>, it will be easy to find and remove such files when
this bug is fixed.
=item *
When using multiple stow directories (see "Multiple stow directories"
in the info manual), Stow fails to "split open" tree-folding symlinks
(see "Installing packages" in the info manual) that point into a stow
directory which is not the one in use by the current Stow
command. Before failing, it should search the target of the link to
see whether any element of the path contains a F<.stow> file. If it
finds one, it can "learn" about the cooperating stow directory to
short-circuit the F<.stow> search the next time it encounters a
tree-folding symlink.
=back
=head1 AUTHOR
This man page was originally constructed by Charles Briscoe-Smith from
parts of Stow's info manual, and then converted to POD format by Adam
Spiers. The info manual contains the following notice, which, as it
says, applies to this manual page, too. The text of the section
entitled "GNU General Public License" can be found in the file
F</usr/share/common-licenses/GPL> on any Debian GNU/Linux system. If
you don't have access to a Debian system, or the GPL is not there,
write to the Free Software Foundation, Inc., 59 Temple Place, Suite
330, Boston, MA, 02111-1307, USA.
=head1 COPYRIGHT
Copyright (C)
1993, 1994, 1995, 1996 by Bob Glickstein <bobg+stow@zanshin.com>;
2000, 2001 by Guillaume Morin;
2007 by Kahlil Hodgson;
2011 by Adam Spiers;
and others.
Permission is granted to make and distribute verbatim copies of this
manual provided the copyright notice and this permission notice are
preserved on all copies.
Permission is granted to copy and distribute modified versions of this
manual under the conditions for verbatim copying, provided also that
the section entitled "GNU General Public License" is included with the
modified manual, and provided that the entire resulting derived work
is distributed under the terms of a permission notice identical to
this one.
Permission is granted to copy and distribute translations of this
manual into another language, under the above conditions for modified
versions, except that this permission notice may be stated in a
translation approved by the Free Software Foundation.
=cut
use strict;
use warnings;
require 5.006_001;
use POSIX qw(getcwd);
use Getopt::Long qw(GetOptionsFromArray);
use Scalar::Util qw(reftype);
@USE_LIB_PMDIR@
use Stow;
use Stow::Util qw(parent error);
my $ProgramName = $0;
$ProgramName =~ s{.*/}{};
main() unless caller();
sub main {
my ($options, $pkgs_to_unstow, $pkgs_to_stow) = process_options();
my $stow = new Stow(%$options);
$stow->plan_unstow(@$pkgs_to_unstow);
$stow->plan_stow (@$pkgs_to_stow);
my %conflicts = $stow->get_conflicts;
if (%conflicts) {
foreach my $action ('unstow', 'stow') {
next unless $conflicts{$action};
foreach my $package (sort keys %{ $conflicts{$action} }) {
warn "WARNING! ${action}ing $package would cause conflicts:\n";
#if $stow->get_action_count > 1;
foreach my $message (sort @{ $conflicts{$action}{$package} }) {
warn " * $message\n";
}
}
}
warn "All operations aborted.\n";
exit 1;
}
else {
if ($options->{simulate}) {
warn "WARNING: in simulation mode so not modifying filesystem.\n";
return;
}
$stow->process_tasks();
}
}
#===== SUBROUTINE ===========================================================
# Name : process_options()
# Purpose : Parse and process command line and .stowrc file options
# Parameters: none
# Returns : (\%options, \@pkgs_to_unstow, \@pkgs_to_stow)
# Throws : a fatal error if a bad option is given
# Comments : checks @ARGV for valid package names
#============================================================================
sub process_options {
# Get cli options.
my ($cli_options,
$pkgs_to_unstow,
$pkgs_to_stow) = parse_options(@ARGV);
# Get the .stowrc options.
# Note that rc_pkgs_to_unstow and rc_pkgs_to_stow are ignored.
my ($rc_options,
$rc_pkgs_to_unstow,
$rc_pkgs_to_stow) = get_config_file_options();
# Merge .stowrc and command line options.
# Preference is given to cli options.
my %options = %$rc_options;
foreach my $option (keys %$cli_options) {
my $rc_value = $rc_options->{$option};
my $cli_value = $cli_options->{$option};
my $type = reftype($cli_value);
if (defined $type && $type eq 'ARRAY' && defined $rc_value) {
# rc options come first in merged arrays.
$options{$option} = [@{$rc_value}, @{$cli_value}];
} else {
# cli options overwrite conflicting rc options.
$options{$option} = $cli_value;
}
}
# Run checks on the merged options.
sanitize_path_options(\%options);
check_packages($pkgs_to_unstow, $pkgs_to_stow);
# Return merged and processed options.
return (\%options, $pkgs_to_unstow, $pkgs_to_stow);
}
#===== SUBROUTINE ===========================================================
# Name : parse_options()
# Purpose : parse command line options
# Parameters: @arg_array => array of options to parse
# Example: parse_options(@ARGV)
# Returns : (\%options, \@pkgs_to_unstow, \@pkgs_to_stow)
# Throws : a fatal error if a bad command line option is given
# Comments : Used for parsing both command line options and rc file. Used
# for parsing only. Sanity checks and post-processing belong in
# process_options().
#============================================================================
sub parse_options {
my %options = ();
my @pkgs_to_unstow = ();
my @pkgs_to_stow = ();
my $action = 'stow';
#$,="\n"; print @_,"\n"; # for debugging rc file
Getopt::Long::config('no_ignore_case', 'bundling', 'permute');
GetOptionsFromArray(
\@_,
\%options,
'verbose|v:+', 'help|h', 'simulate|n|no',
'version|V', 'compat|p', 'dir|d=s', 'target|t=s',
'adopt', 'no-folding', 'dotfiles',
# clean and pre-compile any regex's at parse time
'ignore=s' =>
sub {
my $regex = $_[1];
push @{$options{ignore}}, qr($regex\z);
},
'override=s' =>
sub {
my $regex = $_[1];
push @{$options{override}}, qr(\A$regex);
},
'defer=s' =>
sub {
my $regex = $_[1];
push @{$options{defer}}, qr(\A$regex);
},
# a little craziness so we can do different actions on the same line:
# a -D, -S, or -R changes the action that will be performed on the
# package arguments that follow it.
'D|delete' => sub { $action = 'unstow' },
'S|stow' => sub { $action = 'stow' },
'R|restow' => sub { $action = 'restow' },
# Handler for non-option arguments
'<>' =>
sub {
if ($action eq 'restow') {
push @pkgs_to_unstow, $_[0];
push @pkgs_to_stow, $_[0];
}
elsif ($action eq 'unstow') {
push @pkgs_to_unstow, $_[0];
}
else {
push @pkgs_to_stow, $_[0];
}
},
) or usage('');
usage() if $options{help};
version() if $options{version};
return (\%options, \@pkgs_to_unstow, \@pkgs_to_stow);
}
sub sanitize_path_options {
my ($options) = @_;
unless (exists $options->{dir}) {
$options->{dir} = length $ENV{STOW_DIR} ? $ENV{STOW_DIR} : getcwd();
}
usage("--dir value '$options->{dir}' is not a valid directory")
unless -d $options->{dir};
if (exists $options->{target}) {
usage("--target value '$options->{target}' is not a valid directory")
unless -d $options->{target};
}
else {
$options->{target} = parent($options->{dir}) || '.';
}
}
sub check_packages {
my ($pkgs_to_stow, $pkgs_to_unstow) = @_;
if (not @$pkgs_to_stow and not @$pkgs_to_unstow) {
usage("No packages to stow or unstow");
}
# check package arguments
for my $package (@$pkgs_to_stow, @$pkgs_to_unstow) {
$package =~ s{/+$}{}; # delete trailing slashes
if ($package =~ m{/}) {
error("Slashes are not permitted in package names");
}
}
}
#===== SUBROUTINE ============================================================
# Name : get_config_file_options()
# Purpose : search for default settings in any .stowrc files
# Parameters: none
# Returns : (\%rc_options, \@rc_pkgs_to_unstow, \@rc_pkgs_to_stow)
# Throws : a fatal error if a bad option is given
# Comments : Parses the contents of '~/.stowrc' and '.stowrc' with the same
# parser as the command line options. Additionally expands any
# environment variables or ~ character in --target or --dir
# options.
#=============================================================================
sub get_config_file_options {
my @defaults = ();
my @dirlist = ('.stowrc');
if (defined($ENV{HOME})) {
unshift(@dirlist, "$ENV{HOME}/.stowrc");
}
for my $file (@dirlist) {
if (-r $file) {
open my $FILE, '<', $file
or die "Could not open $file for reading\n";
while (my $line = <$FILE>){
chomp $line;
push @defaults, split " ", $line;
}
close $FILE or die "Could not close open file: $file\n";
}
}
# Parse the options
my ($rc_options, $rc_pkgs_to_unstow, $rc_pkgs_to_stow) = parse_options(@defaults);
# Expand environment variables and glob characters.
if (exists $rc_options->{target}) {
$rc_options->{target} =
expand_filepath($rc_options->{target}, '--target option');
}
if (exists $rc_options->{dir}) {
$rc_options->{dir} =
expand_filepath($rc_options->{dir}, '--dir option');
}
return ($rc_options, $rc_pkgs_to_unstow, $rc_pkgs_to_stow);
}
#===== SUBROUTINE ============================================================
# Name : expand_filepath()
# Purpose : Handles expansions that need to be applied to
# : file paths. Currently expands environment
# : variables and the tilde.
# Parameters: $path => string to perform expansion on.
# : $source => where the string came from
# Returns : String with replacements performed.
# Throws : n/a
# Comments : n/a
#=============================================================================
sub expand_filepath {
my ($path, $source) = @_;
$path = expand_environment($path, $source);
$path = expand_tilde($path);
return $path;
}
#===== SUBROUTINE ============================================================
# Name : expand_environment()
# Purpose : Expands evironment variables.
# Parameters: $path => string to perform expansion on.
# : $source => where the string came from
# Returns : String with replacements performed.
# Throws : n/a
# Comments : Variable replacement mostly based on SO answer
# : http://stackoverflow.com/a/24675093/558820
#=============================================================================
sub expand_environment {
my ($path, $source) = @_;
# Replace non-escaped $VAR and ${VAR} with $ENV{VAR}
# If $ENV{VAR} does not exist, perl will raise a warning
# and then happily treat it as an empty string.
$path =~ s/(?<!\\)\$\{((?:\w|\s)+)\}/
_safe_expand_env_var($1, $source)
/ge;
$path =~ s/(?<!\\)\$(\w+)/
_safe_expand_env_var($1, $source)
/ge;
# Remove \$ escapes.
$path =~ s/\\\$/\$/g;
return $path;
}
sub _safe_expand_env_var {
my ($var, $source) = @_;
unless (exists $ENV{$var}) {
die "$source references undefined environment variable \$$var; " .
"aborting!\n";
}
return $ENV{$var};
}
#===== SUBROUTINE ============================================================
# Name : expand_tilde()
# Purpose : Expands tilde to user's home directory path.
# Parameters: $path => string to perform expansion on.
# Returns : String with replacements performed.
# Throws : n/a
# Comments : http://docstore.mik.ua/orelly/perl4/cook/ch07_04.htm
#=============================================================================
sub expand_tilde {
my ($path) = @_;
# Replace tilde with home path.
$path =~ s{ ^ ~ ( [^/]* ) }
{ $1
? (getpwnam($1))[7]
: ( $ENV{HOME} || $ENV{LOGDIR}
|| (getpwuid($<))[7]
)
}ex;
# Replace espaced tilde with regular tilde.
$path =~ s/\\~/~/g;
return $path
}
#===== SUBROUTINE ===========================================================
# Name : usage()
# Purpose : print program usage message and exit
# Parameters: $msg => string to prepend to the usage message
# Returns : n/a
# Throws : n/a
# Comments : if 'msg' is given, then exit with non-zero status
#============================================================================
sub usage {
my ($msg) = @_;
if ($msg) {
warn "$ProgramName: $msg\n\n";
}
print <<"EOT";
$ProgramName (GNU Stow) version $Stow::VERSION
SYNOPSIS:
$ProgramName [OPTION ...] [-D|-S|-R] PACKAGE ... [-D|-S|-R] PACKAGE ...
OPTIONS:
-d DIR, --dir=DIR Set stow dir to DIR (default is current dir)
-t DIR, --target=DIR Set target to DIR (default is parent of stow dir)
-S, --stow Stow the package names that follow this option
-D, --delete Unstow the package names that follow this option
-R, --restow Restow (like stow -D followed by stow -S)
--ignore=REGEX Ignore files ending in this Perl regex
--defer=REGEX Don't stow files beginning with this Perl regex
if the file is already stowed to another package
--override=REGEX Force stowing files beginning with this Perl regex
if the file is already stowed to another package
--adopt (Use with care!) Import existing files into stow package
from target. Please read docs before using.
--dotfiles Enables special handling for dotfiles that are
Stow packages that start with "dot-" and not "."
-p, --compat Use legacy algorithm for unstowing
-n, --no, --simulate Do not actually make any filesystem changes
-v, --verbose[=N] Increase verbosity (levels are from 0 to 5;
-v or --verbose adds 1; --verbose=N sets level)
-V, --version Show stow version number
-h, --help Show this help
Report bugs to: bug-stow\@gnu.org
Stow home page: <http://www.gnu.org/software/stow/>
General help using GNU software: <http://www.gnu.org/gethelp/>
EOT
exit defined $msg ? 1 : 0;
}
sub version {
print "$ProgramName (GNU Stow) version $Stow::VERSION\n";
exit 0;
}
1; # This file is required by t/stow.t
# Local variables:
# mode: perl
# end:
# vim: ft=perl

12
build-docker.sh Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -eu
version=$( tools/get-version )
imagename=stowtest
image=$imagename:$version
pushd docker
echo "Building Docker image $image ..."
docker build -t $image .
popd

867
configure vendored
View file

@ -1,867 +0,0 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated automatically using autoconf version 2.10
# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
#
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
# Defaults:
ac_help=
ac_default_prefix=/usr/local
# Any additions from configure.in:
# Initialize some variables set by options.
# The variables have the same names as the options, with
# dashes changed to underlines.
build=NONE
cache_file=./config.cache
exec_prefix=NONE
host=NONE
no_create=
nonopt=NONE
no_recursion=
prefix=NONE
program_prefix=NONE
program_suffix=NONE
program_transform_name=s,x,x,
silent=
site=
srcdir=
target=NONE
verbose=
x_includes=NONE
x_libraries=NONE
bindir='${exec_prefix}/bin'
sbindir='${exec_prefix}/sbin'
libexecdir='${exec_prefix}/libexec'
datadir='${prefix}/share'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
libdir='${exec_prefix}/lib'
includedir='${prefix}/include'
oldincludedir='/usr/include'
infodir='${prefix}/info'
mandir='${prefix}/man'
# Initialize some other variables.
subdirs=
MFLAGS= MAKEFLAGS=
ac_prev=
for ac_option
do
# If the previous option needs an argument, assign it.
if test -n "$ac_prev"; then
eval "$ac_prev=\$ac_option"
ac_prev=
continue
fi
case "$ac_option" in
-*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
*) ac_optarg= ;;
esac
# Accept the important Cygnus configure options, so we can diagnose typos.
case "$ac_option" in
-bindir | --bindir | --bindi | --bind | --bin | --bi)
ac_prev=bindir ;;
-bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
bindir="$ac_optarg" ;;
-build | --build | --buil | --bui | --bu)
ac_prev=build ;;
-build=* | --build=* | --buil=* | --bui=* | --bu=*)
build="$ac_optarg" ;;
-cache-file | --cache-file | --cache-fil | --cache-fi \
| --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
ac_prev=cache_file ;;
-cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
| --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
cache_file="$ac_optarg" ;;
-datadir | --datadir | --datadi | --datad | --data | --dat | --da)
ac_prev=datadir ;;
-datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
| --da=*)
datadir="$ac_optarg" ;;
-disable-* | --disable-*)
ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
# Reject names that are not valid shell variable names.
if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
{ echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
fi
ac_feature=`echo $ac_feature| sed 's/-/_/g'`
eval "enable_${ac_feature}=no" ;;
-enable-* | --enable-*)
ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
# Reject names that are not valid shell variable names.
if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
{ echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
fi
ac_feature=`echo $ac_feature| sed 's/-/_/g'`
case "$ac_option" in
*=*) ;;
*) ac_optarg=yes ;;
esac
eval "enable_${ac_feature}='$ac_optarg'" ;;
-exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
| --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
| --exec | --exe | --ex)
ac_prev=exec_prefix ;;
-exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
| --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
| --exec=* | --exe=* | --ex=*)
exec_prefix="$ac_optarg" ;;
-gas | --gas | --ga | --g)
# Obsolete; use --with-gas.
with_gas=yes ;;
-help | --help | --hel | --he)
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat << EOF
Usage: configure [options] [host]
Options: [defaults in brackets after descriptions]
Configuration:
--cache-file=FILE cache test results in FILE
--help print this message
--no-create do not create output files
--quiet, --silent do not print \`checking...' messages
--version print the version of autoconf that created configure
Directory and file names:
--prefix=PREFIX install architecture-independent files in PREFIX
[$ac_default_prefix]
--exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
[same as prefix]
--bindir=DIR user executables in DIR [EPREFIX/bin]
--sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
--libexecdir=DIR program executables in DIR [EPREFIX/libexec]
--datadir=DIR read-only architecture-independent data in DIR
[PREFIX/share]
--sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data in DIR
[PREFIX/com]
--localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
--libdir=DIR object code libraries in DIR [EPREFIX/lib]
--includedir=DIR C header files in DIR [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
--infodir=DIR info documentation in DIR [PREFIX/info]
--mandir=DIR man documentation in DIR [PREFIX/man]
--srcdir=DIR find the sources in DIR [configure dir or ..]
--program-prefix=PREFIX prepend PREFIX to installed program names
--program-suffix=SUFFIX append SUFFIX to installed program names
--program-transform-name=PROGRAM
run sed PROGRAM on installed program names
EOF
cat << EOF
Host type:
--build=BUILD configure for building on BUILD [BUILD=HOST]
--host=HOST configure for HOST [guessed]
--target=TARGET configure for TARGET [TARGET=HOST]
Features and packages:
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--x-includes=DIR X include files are in DIR
--x-libraries=DIR X library files are in DIR
EOF
if test -n "$ac_help"; then
echo "--enable and --with options recognized:$ac_help"
fi
exit 0 ;;
-host | --host | --hos | --ho)
ac_prev=host ;;
-host=* | --host=* | --hos=* | --ho=*)
host="$ac_optarg" ;;
-includedir | --includedir | --includedi | --included | --include \
| --includ | --inclu | --incl | --inc)
ac_prev=includedir ;;
-includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
| --includ=* | --inclu=* | --incl=* | --inc=*)
includedir="$ac_optarg" ;;
-infodir | --infodir | --infodi | --infod | --info | --inf)
ac_prev=infodir ;;
-infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
infodir="$ac_optarg" ;;
-libdir | --libdir | --libdi | --libd)
ac_prev=libdir ;;
-libdir=* | --libdir=* | --libdi=* | --libd=*)
libdir="$ac_optarg" ;;
-libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
| --libexe | --libex | --libe)
ac_prev=libexecdir ;;
-libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
| --libexe=* | --libex=* | --libe=*)
libexecdir="$ac_optarg" ;;
-localstatedir | --localstatedir | --localstatedi | --localstated \
| --localstate | --localstat | --localsta | --localst \
| --locals | --local | --loca | --loc | --lo)
ac_prev=localstatedir ;;
-localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
| --localstate=* | --localstat=* | --localsta=* | --localst=* \
| --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
localstatedir="$ac_optarg" ;;
-mandir | --mandir | --mandi | --mand | --man | --ma | --m)
ac_prev=mandir ;;
-mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
mandir="$ac_optarg" ;;
-nfp | --nfp | --nf)
# Obsolete; use --without-fp.
with_fp=no ;;
-no-create | --no-create | --no-creat | --no-crea | --no-cre \
| --no-cr | --no-c)
no_create=yes ;;
-no-recursion | --no-recursion | --no-recursio | --no-recursi \
| --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
no_recursion=yes ;;
-oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
| --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
| --oldin | --oldi | --old | --ol | --o)
ac_prev=oldincludedir ;;
-oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
| --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
| --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
oldincludedir="$ac_optarg" ;;
-prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
ac_prev=prefix ;;
-prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
prefix="$ac_optarg" ;;
-program-prefix | --program-prefix | --program-prefi | --program-pref \
| --program-pre | --program-pr | --program-p)
ac_prev=program_prefix ;;
-program-prefix=* | --program-prefix=* | --program-prefi=* \
| --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
program_prefix="$ac_optarg" ;;
-program-suffix | --program-suffix | --program-suffi | --program-suff \
| --program-suf | --program-su | --program-s)
ac_prev=program_suffix ;;
-program-suffix=* | --program-suffix=* | --program-suffi=* \
| --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
program_suffix="$ac_optarg" ;;
-program-transform-name | --program-transform-name \
| --program-transform-nam | --program-transform-na \
| --program-transform-n | --program-transform- \
| --program-transform | --program-transfor \
| --program-transfo | --program-transf \
| --program-trans | --program-tran \
| --progr-tra | --program-tr | --program-t)
ac_prev=program_transform_name ;;
-program-transform-name=* | --program-transform-name=* \
| --program-transform-nam=* | --program-transform-na=* \
| --program-transform-n=* | --program-transform-=* \
| --program-transform=* | --program-transfor=* \
| --program-transfo=* | --program-transf=* \
| --program-trans=* | --program-tran=* \
| --progr-tra=* | --program-tr=* | --program-t=*)
program_transform_name="$ac_optarg" ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
| --sbi=* | --sb=*)
sbindir="$ac_optarg" ;;
-sharedstatedir | --sharedstatedir | --sharedstatedi \
| --sharedstated | --sharedstate | --sharedstat | --sharedsta \
| --sharedst | --shareds | --shared | --share | --shar \
| --sha | --sh)
ac_prev=sharedstatedir ;;
-sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
| --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
| --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
| --sha=* | --sh=*)
sharedstatedir="$ac_optarg" ;;
-site | --site | --sit)
ac_prev=site ;;
-site=* | --site=* | --sit=*)
site="$ac_optarg" ;;
-srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
ac_prev=srcdir ;;
-srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
srcdir="$ac_optarg" ;;
-sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
| --syscon | --sysco | --sysc | --sys | --sy)
ac_prev=sysconfdir ;;
-sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
| --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
sysconfdir="$ac_optarg" ;;
-target | --target | --targe | --targ | --tar | --ta | --t)
ac_prev=target ;;
-target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
target="$ac_optarg" ;;
-v | -verbose | --verbose | --verbos | --verbo | --verb)
verbose=yes ;;
-version | --version | --versio | --versi | --vers)
echo "configure generated by autoconf version 2.10"
exit 0 ;;
-with-* | --with-*)
ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
# Reject names that are not valid shell variable names.
if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
{ echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
fi
ac_package=`echo $ac_package| sed 's/-/_/g'`
case "$ac_option" in
*=*) ;;
*) ac_optarg=yes ;;
esac
eval "with_${ac_package}='$ac_optarg'" ;;
-without-* | --without-*)
ac_package=`echo $ac_option|sed -e 's/-*without-//'`
# Reject names that are not valid shell variable names.
if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
{ echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
fi
ac_package=`echo $ac_package| sed 's/-/_/g'`
eval "with_${ac_package}=no" ;;
--x)
# Obsolete; use --with-x.
with_x=yes ;;
-x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
| --x-incl | --x-inc | --x-in | --x-i)
ac_prev=x_includes ;;
-x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
| --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
x_includes="$ac_optarg" ;;
-x-libraries | --x-libraries | --x-librarie | --x-librari \
| --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
ac_prev=x_libraries ;;
-x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries="$ac_optarg" ;;
-*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
;;
*)
if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
echo "configure: warning: $ac_option: invalid host type" 1>&2
fi
if test "x$nonopt" != xNONE; then
{ echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
fi
nonopt="$ac_option"
;;
esac
done
if test -n "$ac_prev"; then
{ echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
fi
trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
# File descriptor usage:
# 0 standard input
# 1 file creation
# 2 errors and warnings
# 3 some systems may open it to /dev/tty
# 4 used on the Kubota Titan
# 6 checking for... messages and results
# 5 compiler messages saved in config.log
if test "$silent" = yes; then
exec 6>/dev/null
else
exec 6>&1
fi
exec 5>./config.log
echo "\
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
" 1>&5
# Strip out --no-create and --no-recursion so they do not pile up.
# Also quote any args containing shell metacharacters.
ac_configure_args=
for ac_arg
do
case "$ac_arg" in
-no-create | --no-create | --no-creat | --no-crea | --no-cre \
| --no-cr | --no-c) ;;
-no-recursion | --no-recursion | --no-recursio | --no-recursi \
| --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
*" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
ac_configure_args="$ac_configure_args '$ac_arg'" ;;
*) ac_configure_args="$ac_configure_args $ac_arg" ;;
esac
done
# NLS nuisances.
# Only set LANG and LC_ALL to C if already set.
# These must not be set unconditionally because not all systems understand
# e.g. LANG=C (notably SCO).
if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
if test "${LANG+set}" = set; then LANG=C; export LANG; fi
# confdefs.h avoids OS command line length limits that DEFS can exceed.
rm -rf conftest* confdefs.h
# AIX cpp loses on an empty file, so make sure it contains at least a newline.
echo > confdefs.h
# A filename unique to this package, relative to the directory that
# configure is in, which we can look for to find out if srcdir is correct.
ac_unique_file=stow.in
# Find the source files, if location was not specified.
if test -z "$srcdir"; then
ac_srcdir_defaulted=yes
# Try the directory containing this script, then its parent.
ac_prog=$0
ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
srcdir=$ac_confdir
if test ! -r $srcdir/$ac_unique_file; then
srcdir=..
fi
else
ac_srcdir_defaulted=no
fi
if test ! -r $srcdir/$ac_unique_file; then
if test "$ac_srcdir_defaulted" = yes; then
{ echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
else
{ echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
fi
fi
srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
# Prefer explicitly selected file to automatically selected ones.
if test -z "$CONFIG_SITE"; then
if test "x$prefix" != xNONE; then
CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
else
CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
fi
fi
for ac_site_file in $CONFIG_SITE; do
if test -r "$ac_site_file"; then
echo "loading site script $ac_site_file"
. "$ac_site_file"
fi
done
if test -r "$cache_file"; then
echo "loading cache $cache_file"
. $cache_file
else
echo "creating cache $cache_file"
> $cache_file
fi
ac_ext=c
# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
ac_cpp='$CPP $CPPFLAGS'
ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
# Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
ac_n= ac_c='
' ac_t=' '
else
ac_n=-n ac_c= ac_t=
fi
else
ac_n= ac_c='\c' ac_t=
fi
PACKAGE=stow
VERSION=1.3.2
if test "$program_transform_name" = s,x,x,; then
program_transform_name=
else
# Double any \ or $. echo might interpret backslashes.
cat <<\EOF_SED > conftestsed
s,\\,\\\\,g; s,\$,$$,g
EOF_SED
program_transform_name="`echo $program_transform_name|sed -f conftestsed`"
rm -f conftestsed
fi
test "$program_prefix" != NONE &&
program_transform_name="s,^,${program_prefix},; $program_transform_name"
# Use a double $ so make ignores it.
test "$program_suffix" != NONE &&
program_transform_name="s,\$\$,${program_suffix},; $program_transform_name"
# sed with no file args requires a program.
test "$program_transform_name" = "" && program_transform_name="s,x,x,"
ac_aux_dir=
for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
if test -f $ac_dir/install-sh; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install-sh -c"
break
elif test -f $ac_dir/install.sh; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install.sh -c"
break
fi
done
if test -z "$ac_aux_dir"; then
{ echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
fi
ac_config_guess=$ac_aux_dir/config.guess
ac_config_sub=$ac_aux_dir/config.sub
ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
# SysV /etc/install, /usr/sbin/install
# SunOS /usr/etc/install
# IRIX /sbin/install
# AIX /bin/install
# AFS /usr/afsws/bin/install, which mishandles nonexistent args
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# ./install, which can be erroneously created by make from ./install.sh.
echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
if test -z "$INSTALL"; then
if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
for ac_dir in $PATH; do
# Account for people who put trailing slashes in PATH elements.
case "$ac_dir/" in
/|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
*)
# OSF1 and SCO ODT 3.0 have their own names for install.
for ac_prog in ginstall installbsd scoinst install; do
if test -f $ac_dir/$ac_prog; then
if test $ac_prog = install &&
grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
# OSF/1 installbsd also uses dspmsg, but is usable.
:
else
ac_cv_path_install="$ac_dir/$ac_prog -c"
break 2
fi
fi
done
;;
esac
done
IFS="$ac_save_ifs"
fi
if test "${ac_cv_path_install+set}" = set; then
INSTALL="$ac_cv_path_install"
else
# As a last resort, use the slow shell script. We don't cache a
# path for INSTALL within a source directory, because that will
# break other packages using the cache if that directory is
# removed, or if the path is relative.
INSTALL="$ac_install_sh"
fi
fi
echo "$ac_t""$INSTALL" 1>&6
# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
# It thinks the first close brace ends the variable substitution.
test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL} -m 755'
for ac_prog in perl perl5 perl4
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
if eval "test \"`echo '$''{'ac_cv_path_PERL'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
case "$PERL" in
/*)
ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
;;
*)
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
for ac_dir in $PATH; do
test -z "$ac_dir" && ac_dir=.
if test -f $ac_dir/$ac_word; then
ac_cv_path_PERL="$ac_dir/$ac_word"
break
fi
done
IFS="$ac_save_ifs"
;;
esac
fi
PERL="$ac_cv_path_PERL"
if test -n "$PERL"; then
echo "$ac_t""$PERL" 1>&6
else
echo "$ac_t""no" 1>&6
fi
test -n "$PERL" && break
done
test -n "$PERL" || PERL="false"
if test "x$PERL" = xfalse
then
echo 'WARNING: Perl not found; you must edit line 1 of `stow'"'"
fi
trap '' 1 2 15
cat > confcache <<\EOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
# scripts and configure runs. It is not useful on other systems.
# If it contains results you don't want to keep, you may remove or edit it.
#
# By default, configure uses ./config.cache as the cache file,
# creating it if it does not exist already. You can give configure
# the --cache-file=FILE option to use a different cache file; that is
# what configure does when it calls configure scripts in
# subdirectories, so they share the cache.
# Giving --cache-file=/dev/null disables caching, for debugging configure.
# config.status only pays attention to the cache file if you give it the
# --recheck option to rerun configure.
#
EOF
# Ultrix sh set writes to stderr and can't be redirected directly,
# and sets the high bit in the cache file unless we assign to the vars.
(set) 2>&1 |
sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \
>> confcache
if cmp -s $cache_file confcache; then
:
else
if test -w $cache_file; then
echo "updating cache $cache_file"
cat confcache > $cache_file
else
echo "not updating unwritable cache $cache_file"
fi
fi
rm -f confcache
trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
test "x$prefix" = xNONE && prefix=$ac_default_prefix
# Let make expand exec_prefix.
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
# Any assignment to VPATH causes Sun make to only execute
# the first set of double-colon rules, so remove it if not needed.
# If there is a colon in the path, we need to keep it.
if test "x$srcdir" = x.; then
ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
fi
trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
# Transform confdefs.h into DEFS.
# Protect against shell expansion while executing Makefile rules.
# Protect against Makefile macro expansion.
cat > conftest.defs <<\EOF
s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g
s%\[%\\&%g
s%\]%\\&%g
s%\$%$$%g
EOF
DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
rm -f conftest.defs
# Without the "./", some shells look in PATH for config.status.
: ${CONFIG_STATUS=./config.status}
echo creating $CONFIG_STATUS
rm -f $CONFIG_STATUS
cat > $CONFIG_STATUS <<EOF
#! /bin/sh
# Generated automatically by configure.
# Run this file to recreate the current configuration.
# This directory was configured as follows,
# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
#
# $0 $ac_configure_args
#
# Compiler output produced by configure, useful for debugging
# configure, is in ./config.log if it exists.
ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
for ac_option
do
case "\$ac_option" in
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
-version | --version | --versio | --versi | --vers | --ver | --ve | --v)
echo "$CONFIG_STATUS generated by autoconf version 2.10"
exit 0 ;;
-help | --help | --hel | --he | --h)
echo "\$ac_cs_usage"; exit 0 ;;
*) echo "\$ac_cs_usage"; exit 1 ;;
esac
done
ac_given_srcdir=$srcdir
ac_given_INSTALL="$INSTALL"
trap 'rm -fr `echo "Makefile stow" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
EOF
cat >> $CONFIG_STATUS <<EOF
# Protect against being on the right side of a sed subst in config.status.
sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
$ac_vpsub
$extrasub
s%@CFLAGS@%$CFLAGS%g
s%@CPPFLAGS@%$CPPFLAGS%g
s%@CXXFLAGS@%$CXXFLAGS%g
s%@DEFS@%$DEFS%g
s%@LDFLAGS@%$LDFLAGS%g
s%@LIBS@%$LIBS%g
s%@exec_prefix@%$exec_prefix%g
s%@prefix@%$prefix%g
s%@program_transform_name@%$program_transform_name%g
s%@bindir@%$bindir%g
s%@sbindir@%$sbindir%g
s%@libexecdir@%$libexecdir%g
s%@datadir@%$datadir%g
s%@sysconfdir@%$sysconfdir%g
s%@sharedstatedir@%$sharedstatedir%g
s%@localstatedir@%$localstatedir%g
s%@libdir@%$libdir%g
s%@includedir@%$includedir%g
s%@oldincludedir@%$oldincludedir%g
s%@infodir@%$infodir%g
s%@mandir@%$mandir%g
s%@PACKAGE@%$PACKAGE%g
s%@VERSION@%$VERSION%g
s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
s%@INSTALL_DATA@%$INSTALL_DATA%g
s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
s%@PERL@%$PERL%g
CEOF
EOF
cat >> $CONFIG_STATUS <<EOF
CONFIG_FILES=\${CONFIG_FILES-"Makefile stow"}
EOF
cat >> $CONFIG_STATUS <<\EOF
for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
# Support "outfile[:infile]", defaulting infile="outfile.in".
case "$ac_file" in
*:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
*) ac_file_in="${ac_file}.in" ;;
esac
# Adjust relative srcdir, etc. for subdirectories.
# Remove last slash and all that follows it. Not all systems have dirname.
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
# The file is in a subdirectory.
test ! -d "$ac_dir" && mkdir "$ac_dir"
ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
# A "../" for each directory in $ac_dir_suffix.
ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
else
ac_dir_suffix= ac_dots=
fi
case "$ac_given_srcdir" in
.) srcdir=.
if test -z "$ac_dots"; then top_srcdir=.
else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
/*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
*) # Relative path.
srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
top_srcdir="$ac_dots$ac_given_srcdir" ;;
esac
case "$ac_given_INSTALL" in
[/$]*) INSTALL="$ac_given_INSTALL" ;;
*) INSTALL="$ac_dots$ac_given_INSTALL" ;;
esac
echo creating "$ac_file"
rm -f "$ac_file"
configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
case "$ac_file" in
*Makefile*) ac_comsub="1i\\
# $configure_input" ;;
*) ac_comsub= ;;
esac
sed -e "$ac_comsub
s%@configure_input@%$configure_input%g
s%@srcdir@%$srcdir%g
s%@top_srcdir@%$top_srcdir%g
s%@INSTALL@%$INSTALL%g
" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file
fi; done
rm -f conftest.subs
exit 0
EOF
chmod +x $CONFIG_STATUS
rm -fr confdefs* $ac_clean_files
test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1

123
configure.ac Normal file
View file

@ -0,0 +1,123 @@
dnl This file is part of GNU Stow.
dnl
dnl GNU Stow is free software: you can redistribute it and/or modify it
dnl under the terms of the GNU General Public License as published by
dnl the Free Software Foundation, either version 3 of the License, or
dnl (at your option) any later version.
dnl
dnl GNU Stow is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dnl General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program. If not, see https://www.gnu.org/licenses/.
dnl Process this file with Autoconf to produce configure dnl
AC_INIT([stow], [2.4.0], [bug-stow@gnu.org])
AC_PREREQ([2.61])
AC_CONFIG_AUX_DIR([automake])
# Unfortunately we have to disable warnings for overrides, because we
# need to override the built-in `check-TESTS' rule and also the TEXI2DVI
# variable.
AM_INIT_AUTOMAKE([-Wall -Werror -Wno-override dist-bzip2 foreign])
AC_PROG_INSTALL
dnl Check for perl on our system.
dnl Call to AC_SUBST(PERL) is implicit
AC_PATH_PROGS([PERL], [perl] [perl5], [false])
if test "x$PERL" = xfalse
then
AC_MSG_ERROR([Perl not found; check your \$PATH.])
fi
missing_test_deps=
for mod in Test::More Test::Output; do
AC_MSG_CHECKING([$mod])
if $PERL -M$mod -e 1 2>/dev/null
then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
missing_test_deps="$missing_test_deps $mod"
fi
done
# N.B. ${var#pattern} will not work with some shells, such as
# Solaris 10's /bin/sh :-(
#
# http://www.gnu.org/software/autoconf/manual/autoconf.html#Portable-Shell
#
# eval `$PERL -V:siteprefix -V:installsitelib`
# pmdir_relative_path="${installsitelib#$siteprefix/}"
#
# This will work:
#
# pmdir_relative_path=`echo "${installsitelib}" | sed -e "s!^$siteprefix/!!"`
#
# but this is cleaner:
pmdir_relative_path=`\
$PERL -MConfig \
-wle '($_ = $Config{installsitelib})
=~ s!^\Q$Config{siteprefix}/!!; \
print'`
AC_ARG_WITH(
[pmdir],
AS_HELP_STRING(
[--with-pmdir=DIR],
[Install Perl modules in DIR]),
[PMDIR=${withval}],
[PMDIR='${prefix}'/"$pmdir_relative_path"])
AC_ARG_ENABLE(
[relative],
AS_HELP_STRING(
[--enable-relative],
[Load Stow modules relative to the main script]),
[FINDBIN="$enable_relative"],
[FINDBIN=no])
AC_CONFIG_COMMANDS_POST([[
eval pmdir="$PMDIR"
cat <<EOF
# Perl modules will be installed to $PMDIR
EOF
if [ "$pmdir" != "$PMDIR" ]; then
cat <<EOF
# which will expand to
#
# $pmdir
#
# unless you override the value of prefix at make-time.
EOF
echo
fi
if test -n "$missing_test_deps"; then
cat <<EOF >&2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! WARNING! $PERL was missing modules:
!
! $missing_test_deps
!
! The test suite will fail. 'make install' may still render
! a working installation, but this cannot be guaranteed.
!
! Please (re-)read INSTALL, then install the missing modules
! and try again.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOF
if test -n "$STRICT_TESTS"; then
exit 1
fi
fi
]])
AC_SUBST([PMDIR])
AC_SUBST([FINDBIN])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View file

@ -1,21 +0,0 @@
dnl Process this file with Autoconf to produce configure
AC_INIT(stow.in)
PACKAGE=stow
VERSION=1.3.2
AC_SUBST(PACKAGE)
AC_SUBST(VERSION)
AC_ARG_PROGRAM
fp_PROG_INSTALL
AC_PATH_PROGS(PERL, perl perl5 perl4, false)
if test "x$PERL" = xfalse
then
echo 'WARNING: Perl not found; you must edit line 1 of `stow'"'"
fi
AC_OUTPUT(Makefile stow)

23
default-ignore-list Normal file
View file

@ -0,0 +1,23 @@
# Comments and blank lines are allowed.
RCS
.+,v
CVS
\.\#.+ # CVS conflict files / emacs lock files
\.cvsignore
\.svn
_darcs
\.hg
\.git
\.gitignore
\.gitmodules
.+~ # emacs backup files
\#.*\# # emacs autosave files
^/README.*
^/LICENSE.*
^/COPYING

View file

@ -1,3 +1,91 @@
Thu Jan 31 2008 Kahlil Hodgson <kal@grebo.cs.rmit.edu.au>
* stow.texi: Austin Wood and Chris Hoobin clean this up for version 2.
* texi2man: new script by Austin and Chris to generate a man page from the
texinfo file.
Sun Nov 25 19:31:32 2007 Kahlil Hodgson <kahlil@internode.con.net>
* all: Version 2.0.1
* AUTHORS: added Kahlil Hodgson as a new author and current maintainer.
* stow.in: major rewrite to produce version 2.0.1 see NEWS for details
* t/: added test suite and support code
* configure.in: renamed to configure.ac as per autotools recommendation.
* configure.ac:
Use AC_INT rather than obsolete AM_INTI_MAKEFILE usage.
Remove redundant VERSION and PACKAGE setttings
Remove redundant AC_ARG_PROGRAM
Use AM_INIT_AUTOMAKE([-Wall -Werror]) because we are pedantic.
Add AC_PREREQ([2.6.1])
* Makefile.am, configure.ac:
Use explicit rewrite in Makefile.am, rather than AC_CONFIG_FILES(stow.in),
as per autotools recommendation.
* Makefile.am:
Add TESTS and TEST_ENVIRONMENT for files in t/
Use dist_man_MANS instead of EXTRA_DIST for man page
* INSTALL: update to reflect autotools modernization.
* NEWS: update to describe cahnges in Version 2.0.1.
* README: update to point to the right websites and email addresses.
* THANKS:
Add Emil Mikulc who's ideas largely inspired Version 2 and
and Geoffrey Giesemann who did some initial testing and found some
important bugs.
* TODO: remove tasks that where implemented in Version 2
* stow.texi: update documentation to reflect Version 2 changes.
* stow.8: update to reflect Version 2 changes.
Sat Jan 26 16:15:21 2002 Guillaume Morin <gmorin@gnu.org>
* stow.in: if $ENV{'STOW_DIR'} is set, this becomes the default
Stow directory.
Sun Jan 06 12:18:50 2002 Guillaume Morin <gmorin@gnu.org>
* Makefile.am: use EXTRA_DIST to include manpage in distribution
Wed Jan 02 21:33:41 2002 Guillaume Morin <gmorin@gnu.org>
* stow.in: Stow now only warns the user if a subdirectory
is unreadable during unstowing.
Wed Jan 02 20:58:05 2002 Guillaume Morin <gmorin@gnu.org>
* stow.in: fixed JoinPaths so that subdirs called "0" are
correctly pushed. Thanks a lot to Gergely Nagy
<algernon@bonehunter.rulez.org> who patiently helped me to chase
this bug.
Sun Dec 30 21:58:25 2001 Guillaume Morin <gmorin@gnu.org>
* stow.in: fixed a bug introduced by previous changes when
Target argument was relative. (thanks to Luca Filipozzi
<lfilipoz@debian.org> for pointing this out)
Sun Dec 30 18:23:25 2001 Guillaume Morin <gmorin@gnu.org>
* stow.in: now requires Perl 5. Use POSIX getcwd instead of broken
fastcwd. Fixed bug when CommonParent is /. Stow does not remove
initially empty directories anymore.
Sun Dec 30 18:07:51 2001 Guillaume Morin <gmorin@gnu.org>
* configure.in: automake fixes (fp_ -> AC, +AC_INIT_AUTOMAKE)
Fri Oct 11 22:09:45 1996 Bob Glickstein <bobg@hiro.zanshin.com>
* stow.html, configure.in: Version 1.3.2.

173
doc/HOWTO-RELEASE Normal file
View file

@ -0,0 +1,173 @@
How to make a new release of GNU Stow
=====================================
Prerequisite reading
--------------------
First read the official information for maintainers of GNU software:
https://www.gnu.org/prep/maintain/
Release procedure
-----------------
- Ensure configure.ac contains the number of the new unreleased
version. This should follow Semantic Versioning as described at:
http://semver.org/
- To make the following steps easier, set the $version shell variable
to the same version number as above, e.g.
version=$( tools/get-version ) && echo $version
- Ensure NEWS contains the latest changes. If necessary, commit
any additions:
git commit -m "Prepare NEWS for $version release"
- Check CPAN distribution will work via Module::Build:
- Start from a clean slate:
make maintainer-clean
autoreconf -iv
- Generate stow, chkstow, and lib/Stow.pm via:
eval `perl -V:siteprefix`
automake --add-missing
./configure --prefix=$siteprefix && make
(N.B. the CPAN distribution will contain these files, whereas
the GNU distribution will not.)
- Make sure all the following commands all run successfully:
perl Build.PL --prefix=/tmp/stow-test
./Build test
./Build install
./Build distcheck
./Build distmeta
./Build dist
- Check META.yml and META.json have the new version number.
They already should if the final step of this document was
carried out after the previous release was publised, but
if not:
git commit -m "Bump version to $version"
- Ensure all changes are committed to git.
- Run make distcheck and ensure that everything looks good.
It should generate the distribution files for you.
- Run the tests on various Perl versions via Docker:
./build-docker.sh
./test-docker.sh
Obviously if there are any failures, they will need to be fixed
first, and then repeat the above steps.
- At this point we have a release candidate. Tag the current git HEAD
with the new version number:
git tag -s v$version -m "Release $version"
- Upload the resulting Stow-v7.8.9.tar.gz to CPAN via https://pause.perl.org/
- Wait until PAUSE has accepted the upload as a valid module. If you
are the maintainer of the module, you should receive two email
notifications: a CPAN upload confirmation, and a PAUSE indexer
report. This provides some valuable final validation, as learnt the
hard way during the non-release of 2.2.1.
- Push HEAD and tag to savannah and GitHub:
git push savannah master
git push --tags savannah
git push github master
git push --tags github
- Upload the new release to ftp.gnu.org. This is easiest using gnupload:
- git clone git://git.savannah.gnu.org/gnulib.git
- Copy gnulib/build-aux/gnupload to somewhere on your $PATH
- Run gnupload --to ftp.gnu.org:stow --symlink-regex stow-7.8.9.tar.*
- Regenerate the documentation for the website:
# First check out gnulib repository via:
# git clone git://git.savannah.gnu.org/gnulib.git
# and stow-web CVS repository via the instructions here:
# https://savannah.gnu.org/cvs/?group=stow
# Set paths:
stow_repo=/path/to/stow/git/repo
stow_web_repo=/path/to/stow-web/CVS/working/dir
gnulib_repo=/path/to/gnulib/git/repo
cd $stow_repo
export GENDOCS_TEMPLATE_DIR=$gnulib_repo/doc
$gnulib_repo/util/gendocs.sh \
-s doc/stow.texi \
-o $stow_web_repo/manual \
--email bug-stow@gnu.org \
stow "GNU Stow manual"
cd $stow_web_repo
# Check that the changes look OK
cvs diff
# Then commit
cvs commit -m "Update manual to v$version"
- Update the news section of stow.html in the stow-web CVS repository
to mention the new release.
cd $stow_web_repo
# Check that the changes look OK
cvs diff
# Then commit
cvs commit -m "Update home page to v$version"
- Send release announcements to
- info-stow@gnu.org
- stow-devel@gnu.org
- info-gnu@gnu.org
- https://savannah.gnu.org/news/?group=stow
See http://www.gnu.org/prep/maintain/html_node/Announcements.html for
more on making release announcements.
Excerpts of NEWS can be formatted for inclusion in the email by
selecting the relevant version subtree via M-x org-mark-element,
minus the "* Changes in version x.y.z", running M-x
org-export-dispatch, and exporting as plain text.
- Update the git repositories to the next expected version, so that anyone
who builds from git gets a version of Stow which is higher than the release
which was just cut:
- Increment the patchlevel of the version number in configure.ac.
- Run this again:
version=$( tools/get-version ) && echo $version
- In order to update META.yml and META.json, repeat the same
procedure listed above, starting at "make distclean" and
finishing after "./Build distmeta".
- Check META.yml and META.json now have the new versions.
- git add configure.ac META.{yml,json}
- git commit -m "Bump version to $version for development of next release"
- git push savannah master
- git push github master

2136
doc/stow.texi Normal file

File diff suppressed because it is too large Load diff

62
docker/Dockerfile Normal file
View file

@ -0,0 +1,62 @@
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
# Build docker image: `docker build -t stowtest`
# Run tests: (from stow src directory)
# `docker run --rm -it -v $(pwd):$(pwd) -w $(pwd) stowtest`
FROM debian:bookworm
RUN DEBIAN_FRONTEND=noninteractive apt-get update -qq
RUN DEBIAN_FRONTEND=noninteractive \
apt-get install -y -q \
autoconf \
bzip2 \
cpanminus \
gawk \
git \
libssl-dev \
make \
patch \
perlbrew \
texinfo \
texlive \
texi2html \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Set up perlbrew
ENV HOME=/root \
PERLBREW_ROOT=/usr/local/perlbrew \
PERLBREW_HOME=/root/.perlbrew \
PERLBREW_PATH=/usr/local/perlbrew/bin
RUN mkdir -p /usr/local/perlbrew /root \
&& perlbrew init \
&& perlbrew install-cpanm \
&& perlbrew install-patchperl
RUN perlbrew install-multiple -j 4 --notest \
perl-5.22.2 \
perl-5.20.3 \
perl-5.18.4 \
perl-5.16.3 \
perl-5.14.4 \
&& perlbrew clean
# Bootstrap the perl environments
COPY ./bootstrap-perls.sh /bootstrap-perls.sh
RUN /bootstrap-perls.sh && rm /bootstrap-perls.sh
# Add test script to container filesystem
COPY ./run-stow-tests.sh /run-stow-tests.sh
ENTRYPOINT ["/run-stow-tests.sh"]

30
docker/bootstrap-perls.sh Executable file
View file

@ -0,0 +1,30 @@
#!/bin/bash
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
# Load perlbrew environment
. /usr/local/perlbrew/etc/bashrc
# For each perl version installed.
for p_version in $(perlbrew list | sed 's/ //g'); do
# Switch to it.
perlbrew use $p_version
# and install the needed modules.
/usr/local/perlbrew/bin/cpanm -n Devel::Cover::Report::Coveralls Test::More Test::Output
done
# Cleanup to remove any temp files.
perlbrew clean

73
docker/run-stow-tests.sh Executable file
View file

@ -0,0 +1,73 @@
#!/bin/bash
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
# Load perlbrew environment
# Load before setting safety to keep
# perlbrew scripts from breaking due to
# unset variables.
. /usr/local/perlbrew/etc/bashrc
# Standard safety protocol
set -ef -o pipefail
IFS=$'\n\t'
test_perl_version () {
perl_version="$1"
perlbrew use $perl_version
echo $(perl --version)
# Install stow
autoreconf --install
eval `perl -V:siteprefix`
./configure --prefix=$siteprefix && make
make cpanm
# Run tests
make distcheck
perl Build.PL && ./Build build && cover -test
./Build distcheck
}
if [[ -n "$LIST_PERL_VERSIONS" ]]; then
echo "Listing Perl versions available from perlbrew ..."
perlbrew list
elif [[ -z "$PERL_VERSION" ]]; then
echo "Testing all versions ..."
for perl_version in $(perlbrew list | sed 's/ //g'); do
test_perl_version $perl_version
done
make distclean
else
echo "Testing with Perl $PERL_VERSION"
# Test a specific version requested via $PERL_VERSION environment
# variable. Make sure set -e doesn't cause us to bail on failure
# before we start an interactive shell.
test_perl_version $PERL_VERSION || :
# N.B. Don't distclean since we probably want to debug this Perl
# version interactively.
cat <<EOF
To run a specific test, type something like:
perl -Ilib -Ibin -It t/cli_options.t
Code can be edited on the host and will immediately take effect inside
this container.
EOF
bash
fi

View file

@ -1,238 +0,0 @@
#! /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

2506
lib/Stow.pm.in Executable file

File diff suppressed because it is too large Load diff

267
lib/Stow/Util.pm.in Normal file
View file

@ -0,0 +1,267 @@
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
package Stow::Util;
=head1 NAME
Stow::Util - general utilities
=head1 SYNOPSIS
use Stow::Util qw(debug set_debug_level error ...);
=head1 DESCRIPTION
Supporting utility routines for L<Stow>.
=cut
use strict;
use warnings;
use File::Spec;
use POSIX qw(getcwd);
use base qw(Exporter);
our @EXPORT_OK = qw(
error debug set_debug_level set_test_mode
join_paths parent canon_path restore_cwd
adjust_dotfile unadjust_dotfile
);
our $ProgramName = 'stow';
our $VERSION = '@VERSION@';
#############################################################################
#
# General Utilities: nothing stow specific here.
#
#############################################################################
=head1 IMPORTABLE SUBROUTINES
=head2 error($format, @args)
Outputs an error message in a consistent form and then dies.
=cut
sub error {
my ($format, @args) = @_;
die "$ProgramName: ERROR: " . sprintf($format, @args) . "\n";
}
=head2 set_debug_level($level)
Sets verbosity level for C<debug()>.
=cut
our $debug_level = 0;
sub set_debug_level {
my ($level) = @_;
$debug_level = $level;
}
=head2 set_test_mode($on_or_off)
Sets testmode on or off.
=cut
our $test_mode = 0;
sub set_test_mode {
my ($on_or_off) = @_;
if ($on_or_off) {
$test_mode = 1;
}
else {
$test_mode = 0;
}
}
=head2 debug($level[, $indent_level], $msg)
Logs to STDERR based on C<$debug_level> setting. C<$level> is the
minimum verbosity level required to output C<$msg>. All output is to
STDERR to preserve backward compatibility, except for in test mode,
when STDOUT is used instead. In test mode, the verbosity can be
overridden via the C<TEST_VERBOSE> environment variable.
Verbosity rules:
=over 4
=item 0: errors only
=item >= 1: print operations: LINK/UNLINK/MKDIR/RMDIR/MV
=item >= 2: print operation exceptions
e.g. "_this_ already points to _that_", skipping, deferring,
overriding, fixing invalid links
=item >= 3: print trace detail: trace: stow/unstow/package/contents/node
=item >= 4: debug helper routines
=item >= 5: debug ignore lists
=back
=cut
sub debug {
my $level = shift;
my $indent_level;
# Maintain backwards-compatibility in case anyone's relying on this.
$indent_level = $_[0] =~ /^\d+$/ ? shift : 0;
my $msg = shift;
if ($debug_level >= $level) {
my $indent = ' ' x $indent_level;
if ($test_mode) {
print "# $indent$msg\n";
}
else {
warn "$indent$msg\n";
}
}
}
#===== METHOD ===============================================================
# Name : join_paths()
# Purpose : concatenates given paths
# Parameters: path1, path2, ... => paths
# Returns : concatenation of given paths
# Throws : n/a
# Comments : Factors out some redundant path elements:
# : '//' => '/', and 'a/b/../c' => 'a/c'. We need this function
# : with this behaviour, even though b could be a symlink to
# : elsewhere, as noted in the perldoc for File::Spec->canonpath().
# : This behaviour is deliberately different to
# : Stow::Util::canon_path(), because the way join_paths() is used
# : relies on this. Firstly, there is no guarantee that the paths
# : exist, so a filesystem check is inappropriate.
# :
# : For example, it's used to determine the path from the target
# : directory to a symlink destination. So if a symlink
# : path/to/target/a/b/c points to ../../../stow/pkg/a/b/c,
# : then joining path/to/target/a/b with ../../../stow/pkg/a/b/c
# : yields path/to/stow/pkg/a/b/c, and it's crucial that the
# : path/to/stow prefix matches a recognisable stow directory.
#============================================================================
sub join_paths {
my @paths = @_;
debug(5, 5, "| Joining: @paths");
my $result = '';
for my $part (@paths) {
next if ! length $part; # probably shouldn't happen?
$part = File::Spec->canonpath($part);
if (substr($part, 0, 1) eq '/') {
$result = $part; # absolute path, so ignore all previous parts
}
else {
$result .= '/' if length $result && $result ne '/';
$result .= $part;
}
debug(7, 6, "| Join now: $result");
}
debug(6, 5, "| Joined: $result");
# Need this to remove any initial ./
$result = File::Spec->canonpath($result);
# remove foo/..
1 while $result =~ s,(^|/)(?!\.\.)[^/]+/\.\.(/|$),$1,;
debug(6, 5, "| After .. removal: $result");
$result = File::Spec->canonpath($result);
debug(5, 5, "| Final join: $result");
return $result;
}
#===== METHOD ===============================================================
# Name : parent
# Purpose : find the parent of the given path
# Parameters: @path => components of the path
# Returns : returns a path string
# Throws : n/a
# Comments : allows you to send multiple chunks of the path
# : (this feature is currently not used)
#============================================================================
sub parent {
my @path = @_;
my $path = join '/', @_;
my @elts = split m{/+}, $path;
pop @elts;
return join '/', @elts;
}
#===== METHOD ===============================================================
# Name : canon_path
# Purpose : find absolute canonical path of given path
# Parameters: $path
# Returns : absolute canonical path
# Throws : n/a
# Comments : is this significantly different from File::Spec->rel2abs?
#============================================================================
sub canon_path {
my ($path) = @_;
my $cwd = getcwd();
chdir($path) or error("canon_path: cannot chdir to $path from $cwd");
my $canon_path = getcwd();
restore_cwd($cwd);
return $canon_path;
}
sub restore_cwd {
my ($prev) = @_;
chdir($prev) or error("Your current directory $prev seems to have vanished");
}
sub adjust_dotfile {
my ($pkg_node) = @_;
(my $adjusted = $pkg_node) =~ s/^dot-([^.])/.$1/;
return $adjusted;
}
# Needed when unstowing with --compat and --dotfiles
sub unadjust_dotfile {
my ($target_node) = @_;
return $target_node if $target_node =~ /^\.\.?$/;
(my $adjusted = $target_node) =~ s/^\./dot-/;
return $adjusted;
}
=head1 BUGS
=head1 SEE ALSO
=cut
1;
# Local variables:
# mode: perl
# end:
# vim: ft=perl

View file

@ -1,91 +0,0 @@
#!/bin/sh
# mdate-sh - get modification time of a file and pretty-print it
# Copyright (C) 1995 Software Foundation, Inc.
# Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, June 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Prevent date giving response in another language.
LANG=C
export LANG
LC_ALL=C
export LC_ALL
LC_TIME=C
export LC_TIME
# Get the extended ls output of the file.
if ls -L /dev/null 1>/dev/null 2>&1; then
set - `ls -L -l $1`
else
set - `ls -l $1`
fi
# The month is at least the fourth argument.
# (3 shifts here, the next inside the loop)
shift
shift
shift
# Find the month. Next argument is day, followed by the year or time.
month=
until test $month
do
shift
case $1 in
Jan) month=January; nummonth=1;;
Feb) month=February; nummonth=2;;
Mar) month=March; nummonth=3;;
Apr) month=April; nummonth=4;;
May) month=May; nummonth=5;;
Jun) month=June; nummonth=6;;
Jul) month=July; nummonth=7;;
Aug) month=August; nummonth=8;;
Sep) month=September; nummonth=9;;
Oct) month=October; nummonth=10;;
Nov) month=November; nummonth=11;;
Dec) month=December; nummonth=12;;
esac
done
day=$2
# Here we have to deal with the problem that the ls output gives either
# the time of day or the year.
case $3 in
*:*) set `date`; eval year=\$$#
case $2 in
Jan) nummonthtod=1;;
Feb) nummonthtod=2;;
Mar) nummonthtod=3;;
Apr) nummonthtod=4;;
May) nummonthtod=5;;
Jun) nummonthtod=6;;
Jul) nummonthtod=7;;
Aug) nummonthtod=8;;
Sep) nummonthtod=9;;
Oct) nummonthtod=10;;
Nov) nummonthtod=11;;
Dec) nummonthtod=12;;
esac
# For the first six month of the year the time notation can also
# be used for files modified in the last year.
if (expr $nummonth \> $nummonthtod) > /dev/null;
then
year=`expr $year - 1`
fi;;
*) year=$3;;
esac
# The result.
echo $day $month $year

View file

@ -1,36 +0,0 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Last modified: 1994-03-25
# Public domain
errstatus=0
for file in ${1+"$@"} ; do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d in ${1+"$@"} ; do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp" 1>&2
mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$?
fi
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# mkinstalldirs ends here

View file

@ -1 +0,0 @@
timestamp

542
stow.in
View file

@ -1,542 +0,0 @@
#!@PERL@
# GNU Stow - manage the installation of multiple software packages
# Copyright 1993, 1994, 1995, 1996 by Bob Glickstein
#
# 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.
$ProgramName = $0;
$ProgramName =~ s,.*/,,;
$Version = '@VERSION@';
$Conflicts = 0;
$Delete = 0;
$NotReally = 0;
$Verbose = 0;
$ReportHelp = 0;
$Stow = &fastcwd;
$Target = undef;
$Restow = 0;
while (@ARGV && ($_ = $ARGV[0]) && /^-/) {
$opt = $';
shift;
last if /^--$/;
if ($opt =~ /^-/) {
$opt = $';
if ($opt =~ /^no?$/i) {
$NotReally = 1;
} elsif ($opt =~ /^c(o(n(f(l(i(c(ts?)?)?)?)?)?)?)?$/i) {
$Conflicts = 1;
$NotReally = 1;
} elsif ($opt =~ /^dir?/i) {
$remainder = $';
if ($remainder =~ /^=/) {
$Stow = $'; # the stuff after the =
} else {
$Stow = shift;
}
} elsif ($opt =~ /^t(a(r(g(et?)?)?)?)?/i) {
$remainder = $';
if ($remainder =~ /^=/) {
$Target = $'; # the stuff after the =
} else {
$Target = shift;
}
} elsif ($opt =~ /^verb(o(se?)?)?/i) {
$remainder = $';
if ($remainder =~ /^=(\d+)/) {
$Verbose = $1;
} else {
++$Verbose;
}
} elsif ($opt =~ /^de(l(e(te?)?)?)?$/i) {
$Delete = 1;
} elsif ($opt =~ /^r(e(s(t(o(w?)?)?)?)?)?$/i) {
$Restow = 1;
} elsif ($opt =~ /^vers(i(on?)?)?$/i) {
&version();
} else {
&usage(($opt =~ /^h(e(lp?)?)?$/) ? undef :
"unknown or ambiguous option: $opt");
}
} else {
@opts = split(//, $opt);
while ($_ = shift(@opts)) {
if ($_ eq 'n') {
$NotReally = 1;
} elsif ($_ eq 'c') {
$Conflicts = 1;
$NotReally = 1;
} elsif ($_ eq 'd') {
$Stow = (join('', @opts) || shift);
@opts = ();
} elsif ($_ eq 't') {
$Target = (join('', @opts) || shift);
@opts = ();
} elsif ($_ eq 'v') {
++$Verbose;
} elsif ($_ eq 'D') {
$Delete = 1;
} elsif ($_ eq 'R') {
$Restow = 1;
} elsif ($_ eq 'V') {
&version();
} else {
&usage(($_ eq 'h') ? undef : "unknown option: $_");
}
}
}
}
&usage("No packages named") unless @ARGV;
$Target = &parent($Stow) unless $Target;
chdir($Target) || die "Cannot chdir to target tree $Target ($!)\n";
$Target = &fastcwd;
foreach $package (@ARGV) {
$package =~ s,/+$,,; # delete trailing slashes
if ($package =~ m,/,) {
die "$ProgramName: slashes not permitted in package names\n";
}
}
if ($Delete || $Restow) {
@Collections = @ARGV;
&Unstow('', &RelativePath($Target, $Stow));
}
if (!$Delete || $Restow) {
foreach $Collection (@ARGV) {
warn "Stowing package $Collection...\n" if $Verbose;
&StowContents($Collection, &RelativePath($Target, $Stow));
}
}
sub CommonParent {
local($dir1, $dir2) = @_;
local($result, $x);
local(@d1) = split(/\/+/, $dir1);
local(@d2) = split(/\/+/, $dir2);
while (@d1 && @d2 && (($x = shift(@d1)) eq shift(@d2))) {
$result .= "$x/";
}
chop($result);
$result;
}
sub RelativePath {
local($a, $b) = @_;
local($c) = &CommonParent($a, $b);
local(@a) = split(/\/+/, $a);
local(@b) = split(/\/+/, $b);
local(@c) = split(/\/+/, $c);
splice(@a, 0, @c + 0);
splice(@b, 0, @c + 0);
unshift(@b, (('..') x (@a + 0)));
&JoinPaths(@b);
}
sub JoinPaths {
local(@paths, @parts);
local ($x, $y);
local($result) = '';
$result = '/' if ($_[0] =~ /^\//);
foreach $x (@_) {
@parts = split(/\/+/, $x);
foreach $y (@parts) {
push(@paths, $y) if $y;
}
}
$result .= join('/', @paths);
}
sub Unstow {
local($targetdir, $stow) = @_;
local(@contents);
local($content);
local($linktarget, $stowmember, $collection);
local(@stowmember);
local($pure, $othercollection) = (1, '');
local($subpure, $subother);
local(@puresubdirs);
return (0, '') if (&JoinPaths($Target, $targetdir) eq $Stow);
return (0, '') if (-e &JoinPaths($Target, $targetdir, '.stow'));
warn sprintf("Unstowing in %s\n", &JoinPaths($Target, $targetdir))
if ($Verbose > 1);
opendir(DIR, &JoinPaths($Target, $targetdir)) ||
die "$ProgramName: Cannot read directory \"$dir\" ($!)\n";
@contents = readdir(DIR);
closedir(DIR);
foreach $content (@contents) {
next if (($content eq '.') || ($content eq '..'));
if (-l &JoinPaths($Target, $targetdir, $content)) {
($linktarget = readlink(&JoinPaths($Target,
$targetdir,
$content)))
|| die sprintf("%s: Cannot read link %s (%s)\n",
$ProgramName,
&JoinPaths($Target, $targetdir, $content),
$!);
if ($stowmember = &FindStowMember(&JoinPaths($Target,
$targetdir),
$linktarget)) {
@stowmember = split(/\/+/, $stowmember);
$collection = shift(@stowmember);
if (grep(($collection eq $_), @Collections)) {
&DoUnlink(&JoinPaths($Target, $targetdir, $content));
} elsif ($pure) {
if ($othercollection) {
$pure = 0 if ($collection ne $othercollection);
} else {
$othercollection = $collection;
}
}
} else {
$pure = 0;
}
} elsif (-d &JoinPaths($Target, $targetdir, $content)) {
($subpure, $subother) = &Unstow(&JoinPaths($targetdir, $content),
&JoinPaths('..', $stow));
if ($subpure) {
push(@puresubdirs, "$content/$subother");
}
if ($pure) {
if ($subpure) {
if ($othercollection) {
if ($subother) {
if ($othercollection ne $subother) {
$pure = 0;
}
}
} elsif ($subother) {
$othercollection = $subother;
}
} else {
$pure = 0;
}
}
} else {
$pure = 0;
}
}
if ((!$pure || !$targetdir) && @puresubdirs) {
&CoalesceTrees($targetdir, $stow, @puresubdirs);
}
($pure, $othercollection);
}
sub CoalesceTrees {
local($parent, $stow, @trees) = @_;
local($tree, $collection, $x);
foreach $x (@trees) {
($tree, $collection) = ($x =~ /^(.*)\/(.*)/);
&EmptyTree(&JoinPaths($Target, $parent, $tree));
&DoRmdir(&JoinPaths($Target, $parent, $tree));
if ($collection) {
&DoLink(&JoinPaths($stow, $collection, $parent, $tree),
&JoinPaths($Target, $parent, $tree));
}
}
}
sub EmptyTree {
local($dir) = @_;
local(@contents);
local($content);
opendir(DIR, $dir)
|| die "$ProgramName: Cannot read directory \"$dir\" ($!)\n";
@contents = readdir(DIR);
closedir(DIR);
foreach $content (@contents) {
next if (($content eq '.') || ($content eq '..'));
if (-l &JoinPaths($dir, $content)) {
&DoUnlink(&JoinPaths($dir, $content));
} elsif (-d &JoinPaths($dir, $content)) {
&EmptyTree(&JoinPaths($dir, $content));
&DoRmdir(&JoinPaths($dir, $content));
} else {
&DoUnlink(&JoinPaths($dir, $content));
}
}
}
sub StowContents {
local($dir, $stow) = @_;
local(@contents);
local($content);
warn "Stowing contents of $dir\n" if ($Verbose > 1);
opendir(DIR, &JoinPaths($Stow, $dir))
|| die "$ProgramName: Cannot read directory \"$dir\" ($!)\n";
@contents = readdir(DIR);
closedir(DIR);
foreach $content (@contents) {
next if (($content eq '.') || ($content eq '..'));
if (-d &JoinPaths($Stow, $dir, $content)) {
&StowDir(&JoinPaths($dir, $content), $stow);
} else {
&StowNondir(&JoinPaths($dir, $content), $stow);
}
}
}
sub StowDir {
local($dir, $stow) = @_;
local(@dir) = split(/\/+/, $dir);
local($collection) = shift(@dir);
local($subdir) = join('/', @dir);
local($linktarget, $stowsubdir);
warn "Stowing directory $dir\n" if ($Verbose > 1);
if (-l &JoinPaths($Target, $subdir)) {
($linktarget = readlink(&JoinPaths($Target, $subdir)))
|| die sprintf("%s: Could not read link %s (%s)\n",
$ProgramName,
&JoinPaths($Target, $subdir),
$!);
($stowsubdir =
&FindStowMember(sprintf('%s/%s', $Target,
join('/', @dir[0..($#dir - 1)])),
$linktarget))
|| (&Conflict($dir, $subdir), return);
if (-e &JoinPaths($Stow, $stowsubdir)) {
if ($stowsubdir eq $dir) {
warn sprintf("%s already points to %s\n",
&JoinPaths($Target, $subdir),
&JoinPaths($Stow, $dir))
if ($Verbose > 2);
return;
}
if (-d &JoinPaths($Stow, $stowsubdir)) {
&DoUnlink(&JoinPaths($Target, $subdir));
&DoMkdir(&JoinPaths($Target, $subdir));
&StowContents($stowsubdir, &JoinPaths('..', $stow));
&StowContents($dir, &JoinPaths('..', $stow));
} else {
(&Conflict($dir, $subdir), return);
}
} else {
&DoUnlink(&JoinPaths($Target, $subdir));
&DoLink(&JoinPaths($stow, $dir),
&JoinPaths($Target, $subdir));
}
} elsif (-e &JoinPaths($Target, $subdir)) {
if (-d &JoinPaths($Target, $subdir)) {
&StowContents($dir, &JoinPaths('..', $stow));
} else {
&Conflict($dir, $subdir);
}
} else {
&DoLink(&JoinPaths($stow, $dir),
&JoinPaths($Target, $subdir));
}
}
sub StowNondir {
local($file, $stow) = @_;
local(@file) = split(/\/+/, $file);
local($collection) = shift(@file);
local($subfile) = join('/', @file);
local($linktarget, $stowsubfile);
if (-l &JoinPaths($Target, $subfile)) {
($linktarget = readlink(&JoinPaths($Target, $subfile)))
|| die sprintf("%s: Could not read link %s (%s)\n",
$ProgramName,
&JoinPaths($Target, $subfile),
$!);
($stowsubfile =
&FindStowMember(sprintf('%s/%s', $Target,
join('/', @file[0..($#file - 1)])),
$linktarget))
|| (&Conflict($file, $subfile), return);
if (-e &JoinPaths($Stow, $stowsubfile)) {
(&Conflict($file, $subfile), return)
unless ($stowsubfile eq $file);
warn sprintf("%s already points to %s\n",
&JoinPaths($Target, $subfile),
&JoinPaths($Stow, $file))
if ($Verbose > 2);
} else {
&DoUnlink(&JoinPaths($Target, $subfile));
&DoLink(&JoinPaths($stow, $file),
&JoinPaths($Target, $subfile));
}
} elsif (-e &JoinPaths($Target, $subfile)) {
&Conflict($file, $subfile);
} else {
&DoLink(&JoinPaths($stow, $file),
&JoinPaths($Target, $subfile));
}
}
sub DoUnlink {
local($file) = @_;
warn "UNLINK $file\n" if $Verbose;
(unlink($file) || die "$ProgramName: Could not unlink $file ($!)\n")
unless $NotReally;
}
sub DoRmdir {
local($dir) = @_;
warn "RMDIR $dir\n" if $Verbose;
(rmdir($dir) || die "$ProgramName: Could not rmdir $dir ($!)\n")
unless $NotReally;
}
sub DoLink {
local($target, $name) = @_;
warn "LINK $name to $target\n" if $Verbose;
(symlink($target, $name) ||
die "$ProgramName: Could not symlink $name to $target ($!)\n")
unless $NotReally;
}
sub DoMkdir {
local($dir) = @_;
warn "MKDIR $dir\n" if $Verbose;
(mkdir($dir, 0777)
|| die "$ProgramName: Could not make directory $dir ($!)\n")
unless $NotReally;
}
sub Conflict {
local($a, $b) = @_;
if ($Conflicts) {
warn sprintf("CONFLICT: %s vs. %s\n", &JoinPaths($Stow, $a),
&JoinPaths($Target, $b));
} else {
die sprintf("%s: CONFLICT: %s vs. %s\n",
$ProgramName,
&JoinPaths($Stow, $a),
&JoinPaths($Target, $b));
}
}
sub FindStowMember {
local($start, $path) = @_;
local(@x) = split(/\/+/, $start);
local(@path) = split(/\/+/, $path);
local($x);
local(@d) = split(/\/+/, $Stow);
while (@path) {
$x = shift(@path);
if ($x eq '..') {
pop(@x);
return '' unless @x;
} elsif ($x) {
push(@x, $x);
}
}
while (@x && @d) {
if (($x = shift(@x)) ne shift(@d)) {
return '';
}
}
return '' if @d;
join('/', @x);
}
sub parent {
local($path) = join('/', @_);
local(@elts) = split(/\/+/, $path);
pop(@elts);
join('/', @elts);
}
sub usage {
local($msg) = shift;
if ($msg) {
print "$ProgramName: $msg\n";
}
print "$ProgramName (GNU Stow) version $Version\n\n";
print "Usage: $ProgramName [OPTION ...] PACKAGE ...\n";
print <<EOT;
-n, --no Do not actually make changes
-c, --conflicts Scan for conflicts, implies -n
-d DIR, --dir=DIR Set stow dir to DIR (default is current dir)
-t DIR, --target=DIR Set target to DIR (default is parent of stow dir)
-v, --verbose[=N] Increase verboseness (levels are 0,1,2,3;
-v or --verbose adds 1; --verbose=N sets level)
-D, --delete Unstow instead of stow
-R, --restow Restow (like stow -D followed by stow)
-V, --version Show Stow version number
-h, --help Show this help
EOT
exit($msg ? 1 : 0);
}
sub version {
print "$ProgramName (GNU Stow) version $Version\n";
exit(0);
}
# This is from Perl 4's fastcwd.pl, by John Bazik.
#
# Usage: $cwd = &fastcwd;
#
# This is a faster version of getcwd. It's also more dangerous
# because you might chdir out of a directory that you can't chdir back
# into.
sub fastcwd {
local($odev, $oino, $cdev, $cino, $tdev, $tino);
local(@path, $path);
local(*DIR);
($cdev, $cino) = stat('.');
for (;;) {
($odev, $oino) = ($cdev, $cino);
chdir('..');
($cdev, $cino) = stat('.');
last if $odev == $cdev && $oino == $cino;
opendir(DIR, '.');
for (;;) {
$_ = readdir(DIR);
next if $_ eq '.';
next if $_ eq '..';
last unless $_;
($tdev, $tino) = lstat($_);
last unless $tdev != $odev || $tino != $oino;
}
closedir(DIR);
unshift(@path, $_);
}
chdir($path = '/' . join('/', @path));
$path;
}
# Local variables:
# mode: perl
# End:

1131
stow.info

File diff suppressed because it is too large Load diff

1201
stow.texi

File diff suppressed because it is too large Load diff

127
t/chkstow.t Executable file
View file

@ -0,0 +1,127 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing cleanup_invalid_links()
#
use strict;
use warnings;
use testutil;
require "chkstow";
use Test::More tests => 7;
use Test::Output;
use English qw(-no_match_vars);
init_test_dirs();
cd("$TEST_DIR/target");
# setup stow directory
make_path('stow');
make_file('stow/.stow');
# perl
make_path('stow/perl/bin');
make_file('stow/perl/bin/perl');
make_file('stow/perl/bin/a2p');
make_path('stow/perl/info');
make_file('stow/perl/info/perl');
make_path('stow/perl/lib/perl');
make_path('stow/perl/man/man1');
make_file('stow/perl/man/man1/perl.1');
# emacs
make_path('stow/emacs/bin');
make_file('stow/emacs/bin/emacs');
make_file('stow/emacs/bin/etags');
make_path('stow/emacs/info');
make_file('stow/emacs/info/emacs');
make_path('stow/emacs/libexec/emacs');
make_path('stow/emacs/man/man1');
make_file('stow/emacs/man/man1/emacs.1');
#setup target directory
make_path('bin');
make_link('bin/a2p', '../stow/perl/bin/a2p');
make_link('bin/emacs', '../stow/emacs/bin/emacs');
make_link('bin/etags', '../stow/emacs/bin/etags');
make_link('bin/perl', '../stow/perl/bin/perl');
make_path('info');
make_link('info/emacs', '../stow/emacs/info/emacs');
make_link('info/perl', '../stow/perl/info/perl');
make_link('lib', 'stow/perl/lib');
make_link('libexec', 'stow/emacs/libexec');
make_path('man');
make_path('man/man1');
make_link('man/man1/emacs', '../../stow/emacs/man/man1/emacs.1');
make_link('man/man1/perl', '../../stow/perl/man/man1/perl.1');
sub run_chkstow() {
process_options();
check_stow();
}
local @ARGV = ('-t', '.', '-b');
stderr_like(
\&run_chkstow,
qr{\Askipping .*stow.*\z}xms,
"Skip directories containing .stow");
# squelch warn so that check_stow doesn't carp about skipping .stow all the time
$SIG{__WARN__} = sub { };
@ARGV = ('-t', '.', '-l');
stdout_like(
\&run_chkstow,
qr{emacs\nperl\nstow\n}xms,
"List packages");
@ARGV = ('-t', '.', '-b');
stdout_like(
\&run_chkstow,
qr{\A\z}xms,
"No bogus links exist");
@ARGV = ('-t', '.', '-a');
stdout_like(
\&run_chkstow,
qr{\A\z}xms,
"No aliens exist");
# Create an alien
make_file('bin/alien');
@ARGV = ('-t', '.', '-a');
stdout_like(
\&run_chkstow,
qr{Unstowed\ file:\ ./bin/alien}xms,
"Aliens exist");
make_invalid_link('bin/link', 'ireallyhopethisfiledoesn/t.exist');
@ARGV = ('-t', '.', '-b');
stdout_like(
\&run_chkstow,
qr{Bogus\ link:\ ./bin/link}xms,
"Bogus links exist");
@ARGV = ('-b');
process_options();
our $Target;
ok($Target == q{/usr/local},
"Default target is /usr/local/");

98
t/cleanup_invalid_links.t Executable file
View file

@ -0,0 +1,98 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing cleanup_invalid_links()
#
use strict;
use warnings;
use Test::More tests => 4;
use English qw(-no_match_vars);
use testutil;
use Stow::Util;
init_test_dirs();
cd("$TEST_DIR/target");
my $stow;
# Note that each of the following tests use a distinct set of files
subtest('nothing to clean in a simple tree' => sub {
plan tests => 1;
make_path('../stow/pkg1/bin1');
make_file('../stow/pkg1/bin1/file1');
make_link('bin1', '../stow/pkg1/bin1');
$stow = new_Stow();
$stow->cleanup_invalid_links('./');
is(
scalar($stow->get_tasks), 0
=> 'nothing to clean'
);
});
subtest('cleanup an orphaned owned link in a simple tree' => sub {
plan tests => 3;
make_path('bin2');
make_path('../stow/pkg2/bin2');
make_file('../stow/pkg2/bin2/file2a');
make_link('bin2/file2a', '../../stow/pkg2/bin2/file2a');
make_invalid_link('bin2/file2b', '../../stow/pkg2/bin2/file2b');
$stow = new_Stow();
$stow->cleanup_invalid_links('bin2');
is($stow->get_conflict_count, 0, 'no conflicts cleaning up bad link');
is(scalar($stow->get_tasks), 1, 'one task cleaning up bad link');
is($stow->link_task_action('bin2/file2b'), 'remove', 'removal task for bad link');
});
subtest("don't cleanup a bad link not owned by stow" => sub {
plan tests => 2;
make_path('bin3');
make_path('../stow/pkg3/bin3');
make_file('../stow/pkg3/bin3/file3a');
make_link('bin3/file3a', '../../stow/pkg3/bin3/file3a');
make_invalid_link('bin3/file3b', '../../empty');
$stow = new_Stow();
$stow->cleanup_invalid_links('bin3');
is($stow->get_conflict_count, 0, 'no conflicts cleaning up bad link not owned by stow');
is(scalar($stow->get_tasks), 0, 'no tasks cleaning up bad link not owned by stow');
});
subtest("don't cleanup a valid link in the target not owned by stow" => sub {
plan tests => 2;
make_path('bin4');
make_path('../stow/pkg4/bin4');
make_file('../stow/pkg4/bin4/file3a');
make_link('bin4/file3a', '../../stow/pkg4/bin4/file3a');
make_file("unowned");
make_link('bin4/file3b', '../unowned');
$stow = new_Stow();
$stow->cleanup_invalid_links('bin4');
is($stow->get_conflict_count, 0, 'no conflicts cleaning up bad link not owned by stow');
is(scalar($stow->get_tasks), 0, 'no tasks cleaning up bad link not owned by stow');
});

69
t/cli.t Executable file
View file

@ -0,0 +1,69 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Test processing of CLI options.
#
use strict;
use warnings;
use File::Basename;
use Test::More tests => 3;
use testutil;
#init_test_dirs();
# Since here we're doing black-box testing on the stow executable,
# this looks like it should be robust:
#
#my $STOW = dirname(__FILE__) . '/../bin/stow';
#
# but unfortunately it breaks things like "make distcheck", which
# builds the stow script into a separate path like
#
# stow-2.3.0/_build/sub/bin
#
# before cd'ing to something like
#
# stow-2.3.0/_build/sub
#
# and then running the tests via:
#
# make check-TESTS
# make[2]: Entering directory '/path/to/stow/src/stow-2.3.0/_build/sub'
# dir=../../t; \
# /usr/bin/perl -Ibin -Ilib -I../../t -MTest::Harness -e 'runtests(@ARGV)' "${dir#./}"/*.t
#
# So the simplest solution is to hardcode an assumption that we run
# tests either from somewhere like this during distcheck:
#
# stow-2.3.0/_build/sub
#
# or from the top of the source tree during development. This can be done
# via the following, which also follows the KISS principle:
my $STOW = "$^X bin/stow";
`$STOW --help`;
is($?, 0, "--help should return 0 exit code");
my $err = `$STOW --foo 2>&1`;
is($? >> 8, 1, "unrecognised option should return 1 exit code");
like($err, qr/^Unknown option: foo$/m, "unrecognised option should be listed");
# vim:ft=perl

112
t/cli_options.t Executable file
View file

@ -0,0 +1,112 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Test processing of CLI options.
#
use strict;
use warnings;
use Test::More tests => 10;
use testutil;
require 'stow';
init_test_dirs();
local @ARGV = (
'-v',
'-d', "$TEST_DIR/stow",
'-t', "$TEST_DIR/target",
'dummy'
);
my ($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{verbose}, 1, 'verbose option');
is($options->{dir}, "$TEST_DIR/stow", 'stow dir option');
my $stow = new_Stow(%$options);
is($stow->{stow_path}, "../stow" => 'stow dir');
is_deeply($pkgs_to_stow, [ 'dummy' ] => 'default to stow');
#
# Check mixed up package options
#
local @ARGV = (
'-v',
'-D', 'd1', 'd2',
'-S', 's1',
'-R', 'r1',
'-D', 'd3',
'-S', 's2', 's3',
'-R', 'r2',
);
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is_deeply($pkgs_to_delete, [ 'd1', 'd2', 'r1', 'd3', 'r2' ] => 'mixed deletes');
is_deeply($pkgs_to_stow, [ 's1', 'r1', 's2', 's3', 'r2' ] => 'mixed stows');
#
# Check setting deferred paths
#
local @ARGV = (
'--defer=man',
'--defer=info',
'dummy'
);
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is_deeply($options->{defer}, [ qr(\Aman), qr(\Ainfo) ] => 'defer man and info');
#
# Check setting override paths
#
local @ARGV = (
'--override=man',
'--override=info',
'dummy'
);
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is_deeply($options->{override}, [qr(\Aman), qr(\Ainfo)] => 'override man and info');
#
# Check setting ignored paths
#
local @ARGV = (
'--ignore=~',
'--ignore=\.#.*',
'dummy'
);
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is_deeply($options->{ignore}, [ qr(~\z), qr(\.#.*\z) ] => 'ignore temp files');
#
# Check that expansion not applied.
#
local @ARGV = (
"--target=$TEST_DIR/".'$HOME',
'dummy'
);
make_path("$TEST_DIR/".'$HOME');
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "$TEST_DIR/".'$HOME', 'no expansion');
remove_dir("$TEST_DIR/".'$HOME');
# vim:ft=perl

44
t/defer.t Executable file
View file

@ -0,0 +1,44 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing defer().
#
use strict;
use warnings;
use testutil;
use Test::More tests => 4;
init_test_dirs();
cd("$TEST_DIR/target");
my $stow;
$stow = new_Stow(defer => [ 'man' ]);
ok($stow->defer('man/man1/file.1') => 'simple success');
$stow = new_Stow(defer => [ 'lib' ]);
ok(! $stow->defer('man/man1/file.1') => 'simple failure');
$stow = new_Stow(defer => [ 'lib', 'man', 'share' ]);
ok($stow->defer('man/man1/file.1') => 'complex success');
$stow = new_Stow(defer => [ 'lib', 'man', 'share' ]);
ok(! $stow->defer('bin/file') => 'complex failure');

235
t/dotfiles.t Executable file
View file

@ -0,0 +1,235 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Test case for dotfiles special processing
#
use strict;
use warnings;
use Test::More tests => 12;
use English qw(-no_match_vars);
use Stow::Util qw(adjust_dotfile unadjust_dotfile);
use testutil;
init_test_dirs();
cd("$TEST_DIR/target");
subtest('adjust_dotfile()', sub {
plan tests => 4;
my @TESTS = (
['file'],
['dot-'],
['dot-.'],
['dot-file', '.file'],
);
for my $test (@TESTS) {
my ($input, $expected) = @$test;
$expected ||= $input;
is(adjust_dotfile($input), $expected);
}
});
subtest('unadjust_dotfile()', sub {
plan tests => 4;
my @TESTS = (
['file'],
['.'],
['..'],
['.file', 'dot-file'],
);
for my $test (@TESTS) {
my ($input, $expected) = @$test;
$expected ||= $input;
is(unadjust_dotfile($input), $expected);
}
});
my $stow;
subtest("stow dot-foo as .foo", sub {
plan tests => 1;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles');
make_file('../stow/dotfiles/dot-foo');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('.foo'),
'../stow/dotfiles/dot-foo',
=> 'processed dotfile'
);
});
subtest("stow dot-foo as dot-foo without --dotfile enabled", sub {
plan tests => 1;
$stow = new_Stow(dir => '../stow', dotfiles => 0);
make_path('../stow/dotfiles');
make_file('../stow/dotfiles/dot-foo');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('dot-foo'),
'../stow/dotfiles/dot-foo',
=> 'unprocessed dotfile'
);
});
subtest("stow dot-emacs dir as .emacs", sub {
plan tests => 1;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles/dot-emacs');
make_file('../stow/dotfiles/dot-emacs/init.el');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('.emacs'),
'../stow/dotfiles/dot-emacs',
=> 'processed dotfile dir'
);
});
subtest("stow dir marked with 'dot' prefix when directory exists in target", sub {
plan tests => 1;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles/dot-emacs.d');
make_file('../stow/dotfiles/dot-emacs.d/init.el');
make_path('.emacs.d');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('.emacs.d/init.el'),
'../../stow/dotfiles/dot-emacs.d/init.el',
=> 'processed dotfile dir when dir exists (1 level)'
);
});
subtest("stow dir marked with 'dot' prefix when directory exists in target (2 levels)", sub {
plan tests => 1;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles/dot-emacs.d/dot-emacs.d');
make_file('../stow/dotfiles/dot-emacs.d/dot-emacs.d/init.el');
make_path('.emacs.d');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('.emacs.d/.emacs.d'),
'../../stow/dotfiles/dot-emacs.d/dot-emacs.d',
=> 'processed dotfile dir exists (2 levels)'
);
});
subtest("stow dir marked with 'dot' prefix when directory exists in target", sub {
plan tests => 1;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles/dot-one/dot-two');
make_file('../stow/dotfiles/dot-one/dot-two/three');
make_path('.one/.two');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('./.one/.two/three'),
'../../../stow/dotfiles/dot-one/dot-two/three',
=> 'processed dotfile 2 dir exists (2 levels)'
);
});
subtest("dot-. should not have that part expanded.", sub {
plan tests => 2;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles');
make_file('../stow/dotfiles/dot-');
make_path('../stow/dotfiles/dot-.');
make_file('../stow/dotfiles/dot-./foo');
$stow->plan_stow('dotfiles');
$stow->process_tasks();
is(
readlink('dot-'),
'../stow/dotfiles/dot-',
=> 'processed dotfile'
);
is(
readlink('dot-.'),
'../stow/dotfiles/dot-.',
=> 'unprocessed dotfile'
);
});
subtest("unstow .bar from dot-bar", sub {
plan tests => 3;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles');
make_file('../stow/dotfiles/dot-bar');
make_link('.bar', '../stow/dotfiles/dot-bar');
$stow->plan_unstow('dotfiles');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
ok(-f '../stow/dotfiles/dot-bar', 'package file untouched');
ok(! -e '.bar' => '.bar was unstowed');
});
subtest("unstow dot-emacs.d/init.el when .emacs.d/init.el in target", sub {
plan tests => 4;
$stow = new_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles/dot-emacs.d');
make_file('../stow/dotfiles/dot-emacs.d/init.el');
make_path('.emacs.d');
make_link('.emacs.d/init.el', '../../stow/dotfiles/dot-emacs.d/init.el');
$stow->plan_unstow('dotfiles');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
ok(-f '../stow/dotfiles/dot-emacs.d/init.el');
ok(! -e '.emacs.d/init.el', '.emacs.d/init.el unstowed');
ok(-d '.emacs.d/' => '.emacs.d left behind');
});
subtest("unstow dot-emacs.d/init.el in --compat mode", sub {
plan tests => 4;
$stow = new_compat_Stow(dir => '../stow', dotfiles => 1);
make_path('../stow/dotfiles/dot-emacs.d');
make_file('../stow/dotfiles/dot-emacs.d/init.el');
make_path('.emacs.d');
make_link('.emacs.d/init.el', '../../stow/dotfiles/dot-emacs.d/init.el');
$stow->plan_unstow('dotfiles');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
ok(-f '../stow/dotfiles/dot-emacs.d/init.el');
ok(! -e '.emacs.d/init.el', '.emacs.d/init.el unstowed');
ok(-d '.emacs.d/' => '.emacs.d left behind');
});

193
t/examples.t Executable file
View file

@ -0,0 +1,193 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing examples from the documentation
#
use strict;
use warnings;
use testutil;
use Test::More tests => 10;
use English qw(-no_match_vars);
init_test_dirs();
cd("$TEST_DIR/target");
my $stow;
## set up some fake packages to stow
# perl
make_path('stow/perl/bin');
make_file('stow/perl/bin/perl');
make_file('stow/perl/bin/a2p');
make_path('stow/perl/info');
make_file('stow/perl/info/perl');
make_path('stow/perl/lib/perl');
make_path('stow/perl/man/man1');
make_file('stow/perl/man/man1/perl.1');
# emacs
make_path('stow/emacs/bin');
make_file('stow/emacs/bin/emacs');
make_file('stow/emacs/bin/etags');
make_path('stow/emacs/info');
make_file('stow/emacs/info/emacs');
make_path('stow/emacs/libexec/emacs');
make_path('stow/emacs/man/man1');
make_file('stow/emacs/man/man1/emacs.1');
#
# stow perl into an empty target
#
$stow = new_Stow(dir => 'stow');
$stow->plan_stow('perl');
$stow->process_tasks();
ok(
$stow->get_conflict_count == 0 &&
-l 'bin' && -l 'info' && -l 'lib' && -l 'man' &&
readlink('bin') eq 'stow/perl/bin' &&
readlink('info') eq 'stow/perl/info' &&
readlink('lib') eq 'stow/perl/lib' &&
readlink('man') eq 'stow/perl/man'
=> 'stow perl into an empty target'
);
#
# stow perl into a non-empty target
#
# clean up previous stow
remove_link('bin');
remove_link('info');
remove_link('lib');
remove_link('man');
make_path('bin');
make_path('lib');
make_path('man/man1');
$stow = new_Stow(dir => 'stow');
$stow->plan_stow('perl');
$stow->process_tasks();
ok(
$stow->get_conflict_count == 0 &&
-d 'bin' && -d 'lib' && -d 'man' && -d 'man/man1' &&
-l 'info' && -l 'bin/perl' && -l 'bin/a2p' &&
-l 'lib/perl' && -l 'man/man1/perl.1' &&
readlink('info') eq 'stow/perl/info' &&
readlink('bin/perl') eq '../stow/perl/bin/perl' &&
readlink('bin/a2p') eq '../stow/perl/bin/a2p' &&
readlink('lib/perl') eq '../stow/perl/lib/perl' &&
readlink('man/man1/perl.1') eq '../../stow/perl/man/man1/perl.1'
=> 'stow perl into a non-empty target'
);
#
# Install perl into an empty target and then install emacs
#
# clean up previous stow
remove_link('info');
remove_dir('bin');
remove_dir('lib');
remove_dir('man');
$stow = new_Stow(dir => 'stow');
$stow->plan_stow('perl', 'emacs');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'no conflicts');
ok(
-d 'bin' &&
-l 'bin/perl' &&
-l 'bin/emacs' &&
-l 'bin/a2p' &&
-l 'bin/etags' &&
readlink('bin/perl') eq '../stow/perl/bin/perl' &&
readlink('bin/a2p') eq '../stow/perl/bin/a2p' &&
readlink('bin/emacs') eq '../stow/emacs/bin/emacs' &&
readlink('bin/etags') eq '../stow/emacs/bin/etags' &&
-d 'info' &&
-l 'info/perl' &&
-l 'info/emacs' &&
readlink('info/perl') eq '../stow/perl/info/perl' &&
readlink('info/emacs') eq '../stow/emacs/info/emacs' &&
-d 'man' &&
-d 'man/man1' &&
-l 'man/man1/perl.1' &&
-l 'man/man1/emacs.1' &&
readlink('man/man1/perl.1') eq '../../stow/perl/man/man1/perl.1' &&
readlink('man/man1/emacs.1') eq '../../stow/emacs/man/man1/emacs.1' &&
-l 'lib' &&
-l 'libexec' &&
readlink('lib') eq 'stow/perl/lib' &&
readlink('libexec') eq 'stow/emacs/libexec' &&
1
=> 'stow perl into an empty target, then stow emacs'
);
#
# BUG 1:
# 1. stowing a package with an empty directory
# 2. stow another package with the same directory but non empty
# 3. unstow the second package
# Q. the original empty directory should remain
# behaviour is the same as if the empty directory had nothing to do with stow
#
make_path('stow/pkg1a/bin1');
make_path('stow/pkg1b/bin1');
make_file('stow/pkg1b/bin1/file1b');
$stow = new_Stow(dir => 'stow');
$stow->plan_stow('pkg1a', 'pkg1b');
$stow->plan_unstow('pkg1b');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'no conflicts stowing empty dirs');
ok(-d 'bin1' => 'bug 1: stowing empty dirs');
#
# BUG 2: split open tree-folding symlinks pointing inside different stow
# directories
#
make_path('stow2a/pkg2a/bin2');
make_file('stow2a/pkg2a/bin2/file2a');
make_file('stow2a/.stow');
make_path('stow2b/pkg2b/bin2');
make_file('stow2b/pkg2b/bin2/file2b');
make_file('stow2b/.stow');
$stow = new_Stow(dir => 'stow2a');
$stow->plan_stow('pkg2a');
$stow->set_stow_dir('stow2b');
$stow->plan_stow('pkg2b');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'no conflicts splitting tree-folding symlinks');
ok(-d 'bin2' => 'tree got split by packages from multiple stow directories');
ok(-f 'bin2/file2a' => 'file from 1st stow dir');
ok(-f 'bin2/file2b' => 'file from 2nd stow dir');
## Finish this test

148
t/find_stowed_path.t Executable file
View file

@ -0,0 +1,148 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing Stow:: find_stowed_path()
#
use strict;
use warnings;
use Test::More tests => 10;
use testutil;
use Stow::Util qw(set_debug_level);
init_test_dirs();
subtest("find link to a stowed path with relative target" => sub {
plan tests => 3;
# This is a relative path, unlike $ABS_TEST_DIR below.
my $target = "$TEST_DIR/target";
my $stow = new_Stow(dir => "$TEST_DIR/stow", target => $target);
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../../stow/a/b/c");
is($path, "../stow/a/b/c", "path");
is($stow_path, "../stow", "stow path");
is($package, "a", "package");
});
my $stow = new_Stow(dir => "$ABS_TEST_DIR/stow", target => "$ABS_TEST_DIR/target");
# Required by creation of stow2 and stow2/.stow below
cd("$ABS_TEST_DIR/target");
subtest("find link to a stowed path" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../../stow/a/b/c");
is($path, "../stow/a/b/c", "path from target directory");
is($stow_path, "../stow", "stow path from target directory");
is($package, "a", "from target directory");
});
subtest("find link to alien path not owned by Stow" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../alien");
is($path, "", "alien is not stowed, so path is empty");
is($stow_path, "", "alien, so stow path is empty");
is($package, "", "alien is not stowed in any package");
});
# Make a second stow directory within the target directory, so that we
# can check that links to package files within that stow directory are
# detected correctly.
make_path("stow2");
# However this second stow directory is still "alien" to stow until we
# put a .stow file in it. So first test a symlink pointing to a path
# within this second stow directory
subtest("second stow dir still alien without .stow" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../stow2/a/b/c");
is($path, "", "stow2 not a stow dir yet, so path is empty");
is($stow_path, "", "stow2 not a stow dir yet so stow path is empty");
is($package, "", "not stowed in any recognised package yet");
});
# Now make stow2 a secondary stow directory and test that
make_file("stow2/.stow");
subtest(".stow makes second stow dir owned by Stow" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../stow2/a/b/c");
is($path, "stow2/a/b/c", "path");
is($stow_path, "stow2", "stow path");
is($package, "a", "detect alternate stow directory");
});
subtest("relative symlink pointing to target dir" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../..");
# Technically the target dir is not owned by Stow, since
# Stow won't touch the target dir itself, only its contents.
is($path, "", "path");
is($stow_path, "", "stow path");
is($package, "", "corner case - link points to target dir");
});
subtest("relative symlink pointing to parent of target dir" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../../..");
is($path, "", "path");
is($stow_path, "", "stow path");
is($package, "", "corner case - link points to parent of target dir");
});
subtest("unowned symlink pointing to absolute path inside target" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "$ABS_TEST_DIR/target/d");
is($path, "", "path");
is($stow_path, "", "stow path");
is($package, "", "symlink unowned by Stow points to absolute path outside target directory");
});
subtest("unowned symlink pointing to absolute path outside target" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "/dev/null");
is($path, "", "path");
is($stow_path, "", "stow path");
is($package, "", "symlink unowned by Stow points to absolute path outside target directory");
});
# Now make stow2 the primary stow directory and test that it still
# works when the stow directory is under the target directory
$stow->set_stow_dir("$ABS_TEST_DIR/target/stow2");
subtest("stow2 becomes the primary stow directory" => sub {
plan tests => 3;
my ($path, $stow_path, $package) =
$stow->find_stowed_path("a/b/c", "../../stow2/a/b/c");
is($path, "stow2/a/b/c", "path in stow2");
is($stow_path, "stow2", "stow path for stow2");
is($package, "a", "stow2 is subdir of target directory");
});

82
t/foldable.t Executable file
View file

@ -0,0 +1,82 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing foldable()
#
use strict;
use warnings;
use testutil;
use Test::More tests => 4;
use English qw(-no_match_vars);
init_test_dirs();
cd("$TEST_DIR/target");
my $stow = new_Stow(dir => '../stow');
# Note that each of the following tests use a distinct set of files
#
# can fold a simple tree
#
make_path('../stow/pkg1/bin1');
make_file('../stow/pkg1/bin1/file1');
make_path('bin1');
make_link('bin1/file1','../../stow/pkg1/bin1/file1');
is( $stow->foldable('bin1'), '../stow/pkg1/bin1' => q(can fold a simple tree) );
#
# can't fold an empty directory
#
make_path('../stow/pkg2/bin2');
make_file('../stow/pkg2/bin2/file2');
make_path('bin2');
is( $stow->foldable('bin2'), '' => q(can't fold an empty directory) );
#
# can't fold if dir contains a non-link
#
make_path('../stow/pkg3/bin3');
make_file('../stow/pkg3/bin3/file3');
make_path('bin3');
make_link('bin3/file3','../../stow/pkg3/bin3/file3');
make_file('bin3/non-link');
is( $stow->foldable('bin3'), '' => q(can't fold a dir containing non-links) );
#
# can't fold if links point to different directories
#
make_path('bin4');
make_path('../stow/pkg4a/bin4');
make_file('../stow/pkg4a/bin4/file4a');
make_link('bin4/file4a','../../stow/pkg4a/bin4/file4a');
make_path('../stow/pkg4b/bin4');
make_file('../stow/pkg4b/bin4/file4b');
make_link('bin4/file4b','../../stow/pkg4b/bin4/file4b');
is( $stow->foldable('bin4'), '' => q(can't fold if links point to different dirs) );

306
t/ignore.t Executable file
View file

@ -0,0 +1,306 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing ignore lists.
#
use strict;
use warnings;
use File::Temp qw(tempdir);
use Test::More tests => 287;
use testutil;
use Stow::Util qw(join_paths);
init_test_dirs();
cd("$TEST_DIR/target");
my $stow = new_Stow();
sub test_ignores {
my ($stow_path, $package, $context, @tests) = @_;
$context ||= '';
while (@tests) {
my $path = shift @tests;
my $should_ignore = shift @tests;
my $not = $should_ignore ? '' : ' not';
my $was_ignored = $stow->ignore($stow_path, $package, $path);
is(
$was_ignored, $should_ignore,
"Should$not ignore $path $context"
);
}
}
sub test_local_ignore_list_always_ignored_at_top_level {
my ($stow_path, $package, $context) = @_;
test_ignores(
$stow_path, $package, $context,
$Stow::LOCAL_IGNORE_FILE => 1,
"subdir/" . $Stow::LOCAL_IGNORE_FILE => 0,
);
}
sub test_built_in_list {
my ($stow_path, $package, $context, $expect_ignores) = @_;
for my $ignored ('CVS', '.cvsignore', '#autosave#') {
for my $path ($ignored, "foo/bar/$ignored") {
my $suffix = "$path.suffix";
(my $prefix = $path) =~ s!([^/]+)$!prefix.$1!;
test_ignores(
$stow_path, $package, $context,
$path => $expect_ignores,
$prefix => 0,
$suffix => 0,
);
}
}
# The pattern catching lock files allows suffixes but not prefixes
for my $ignored ('.#lock-file') {
for my $path ($ignored, "foo/bar/$ignored") {
my $suffix = "$path.suffix";
(my $prefix = $path) =~ s!([^/]+)$!prefix.$1!;
test_ignores(
$stow_path, $package, $context,
$path => $expect_ignores,
$prefix => 0,
$suffix => $expect_ignores,
);
}
}
}
sub test_user_global_list {
my ($stow_path, $package, $context, $expect_ignores) = @_;
for my $path ('', 'foo/bar/') {
test_ignores(
$stow_path, $package, $context,
$path . 'exact' => $expect_ignores,
$path . '0exact' => 0,
$path . 'exact1' => 0,
$path . '0exact1' => 0,
$path . 'substring' => 0,
$path . '0substring' => 0,
$path . 'substring1' => 0,
$path . '0substring1' => $expect_ignores,
);
}
}
sub setup_user_global_list {
# Now test with global ignore list in home directory
$ENV{HOME} = tempdir();
make_file(join_paths($ENV{HOME}, $Stow::GLOBAL_IGNORE_FILE), <<EOF);
exact
.+substring.+ # here's a comment
.+\.extension
myprefix.+ #hi mum
EOF
}
sub setup_package_local_list {
my ($stow_path, $package, $list) = @_;
my $package_path = join_paths($stow_path, $package);
make_path($package_path);
my $local_ignore = join_paths($package_path, $Stow::LOCAL_IGNORE_FILE);
make_file($local_ignore, $list);
$stow->invalidate_memoized_regexp($local_ignore);
return $local_ignore;
}
sub main {
my $stow_path = '../stow';
my $package;
my $context;
# Test built-in list first. init_test_dirs() already set
# $ENV{HOME} to ensure that we're not using the user's global
# ignore list.
$package = 'non-existent-package';
$context = "when using built-in list";
test_local_ignore_list_always_ignored_at_top_level($stow_path, $package, $context);
test_built_in_list($stow_path, $package, $context, 1);
# Test ~/.stow-global-ignore
setup_user_global_list();
$context = "when using ~/$Stow::GLOBAL_IGNORE_FILE";
test_local_ignore_list_always_ignored_at_top_level($stow_path, $package, $context);
test_built_in_list($stow_path, $package, $context, 0);
test_user_global_list($stow_path, $package, $context, 1);
# Test empty package-local .stow-local-ignore
$package = 'ignorepkg';
my $local_ignore = setup_package_local_list($stow_path, $package, "");
$context = "when using empty $local_ignore";
test_local_ignore_list_always_ignored_at_top_level($stow_path, $package, $context);
test_built_in_list($stow_path, $package, $context, 0);
test_user_global_list($stow_path, $package, $context, 0);
test_ignores(
$stow_path, $package, $context,
'random' => 0,
'foo2/bar' => 0,
'foo2/bars' => 0,
'foo2/bar/random' => 0,
'foo2/bazqux' => 0,
'xfoo2/bazqux' => 0,
);
# Test package-local .stow-local-ignore with only path segment regexps
$local_ignore = setup_package_local_list($stow_path, $package, <<EOF);
random
EOF
$context = "when using $local_ignore with only path segment regexps";
test_local_ignore_list_always_ignored_at_top_level($stow_path, $package, $context);
test_built_in_list($stow_path, $package, $context, 0);
test_user_global_list($stow_path, $package, $context, 0);
test_ignores(
$stow_path, $package, $context,
'random' => 1,
'foo2/bar' => 0,
'foo2/bars' => 0,
'foo2/bar/random' => 1,
'foo2/bazqux' => 0,
'xfoo2/bazqux' => 0,
);
# Test package-local .stow-local-ignore with only full path regexps
$local_ignore = setup_package_local_list($stow_path, $package, <<EOF);
foo2/bar
EOF
$context = "when using $local_ignore with only full path regexps";
test_local_ignore_list_always_ignored_at_top_level($stow_path, $package, $context);
test_built_in_list($stow_path, $package, $context, 0);
test_user_global_list($stow_path, $package, $context, 0);
test_ignores(
$stow_path, $package, $context,
'random' => 0,
'foo2/bar' => 1,
'foo2/bars' => 0,
'foo2/bar/random' => 1,
'foo2/bazqux' => 0,
'xfoo2/bazqux' => 0,
);
# Test package-local .stow-local-ignore with a mixture of regexps
$local_ignore = setup_package_local_list($stow_path, $package, <<EOF);
foo2/bar
random
foo2/baz.+
EOF
$context = "when using $local_ignore with mixture of regexps";
test_local_ignore_list_always_ignored_at_top_level($stow_path, $package, $context);
test_built_in_list($stow_path, $package, $context, 0);
test_user_global_list($stow_path, $package, $context, 0);
test_ignores(
$stow_path, $package, $context,
'random' => 1,
'foo2/bar' => 1,
'foo2/bars' => 0,
'foo2/bar/random' => 1,
'foo2/bazqux' => 1,
'xfoo2/bazqux' => 0,
);
test_examples_in_manual($stow_path);
test_invalid_regexp($stow_path, "Invalid segment regexp in list", <<EOF);
this one's ok
this one isn't|*!
but this one is
EOF
test_invalid_regexp($stow_path, "Invalid full path regexp in list", <<EOF);
this one's ok
this/one isn't|*!
but this one is
EOF
test_ignore_via_stow($stow_path);
}
sub test_examples_in_manual {
my ($stow_path) = @_;
my $package = 'ignorepkg';
my $context = "(example from manual)";
for my $re ('bazqux', 'baz.*', '.*qux', 'bar/.*x', '^/foo/.*qux') {
my $local_ignore = setup_package_local_list($stow_path, $package, "$re\n");
test_ignores(
$stow_path, $package, $context,
"foo/bar/bazqux" => 1,
);
}
for my $re ('bar', 'baz', 'qux', 'o/bar/b') {
my $local_ignore = setup_package_local_list($stow_path, $package, "$re\n");
test_ignores(
$stow_path, $package, $context,
"foo/bar/bazqux" => 0,
);
}
}
sub test_invalid_regexp {
my ($stow_path, $context, $list) = @_;
my $package = 'ignorepkg';
my $local_ignore = setup_package_local_list($stow_path, $package, $list);
eval {
test_ignores(
$stow_path, $package, $context,
"foo/bar/bazqux" => 1,
);
};
like($@, qr/^Failed to compile regexp: Quantifier follows nothing in regex;/,
$context);
}
sub test_ignore_via_stow {
my ($stow_path) = @_;
my $package = 'pkg1';
make_path("$stow_path/$package/foo/bar");
make_file("$stow_path/$package/foo/bar/baz");
setup_package_local_list($stow_path, $package, 'foo');
$stow->plan_stow($package);
is($stow->get_tasks(), 0, 'top dir ignored');
is($stow->get_conflicts(), 0, 'top dir ignored, no conflicts');
make_path("foo");
for my $ignore ('bar', 'foo/bar', '/foo/bar', '^/foo/bar', '^/fo.+ar') {
setup_package_local_list($stow_path, $package, $ignore);
$stow->plan_stow($package);
is($stow->get_tasks(), 0, "bar ignored via $ignore");
is($stow->get_conflicts(), 0, 'bar ignored, no conflicts');
}
make_file("$stow_path/$package/foo/qux");
$stow->plan_stow($package);
$stow->process_tasks();
is($stow->get_conflicts(), 0, 'no conflicts stowing qux');
ok(! -e "foo/bar", "bar ignore prevented stow");
ok(-l "foo/qux", "qux not ignored and stowed");
is(readlink("foo/qux"), "../$stow_path/$package/foo/qux", "qux stowed correctly");
}
main();

61
t/join_paths.t Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing join_paths();
#
use strict;
use warnings;
use Stow::Util qw(join_paths set_debug_level);
#set_debug_level(4);
use Test::More tests => 22;
my @TESTS = (
[['a/b/c', 'd/e/f'], 'a/b/c/d/e/f' => 'simple'],
[['a/b/c', '/d/e/f'], '/d/e/f' => 'relative then absolute'],
[['/a/b/c', 'd/e/f'], '/a/b/c/d/e/f' => 'absolute then relative'],
[['/a/b/c', '/d/e/f'], '/d/e/f' => 'two absolutes'],
[['/a/b/c/', '/d/e/f/'], '/d/e/f' => 'two absolutes with trailing /'],
[['///a/b///c//', '/d///////e/f'], '/d/e/f' => "multiple /'s, absolute"],
[['///a/b///c//', 'd///////e/f'], '/a/b/c/d/e/f' => "multiple /'s, relative"],
[['', 'a/b/c'], 'a/b/c' => 'first empty'],
[['a/b/c', ''], 'a/b/c' => 'second empty'],
[['/', 'a/b/c'], '/a/b/c' => 'first is /'],
[['a/b/c', '/'], '/' => 'second is /'],
[['../a1/b1/../c1/', 'a2/../b2/e2'], '../a1/c1/b2/e2' => 'relative with ../'],
[['../a1/b1/../c1/', '/a2/../b2/e2'], '/b2/e2' => 'absolute with ../'],
[['../a1/../../c1', 'a2/../../'], '../..' => 'lots of ../'],
[['./', '../a2'], '../a2' => 'drop any "./"'],
[['./a1', '../../a2'], '../a2' => 'drop any "./foo"'],
[['a/b/c', '.'], 'a/b/c' => '. on RHS'],
[['a/b/c', '.', 'd/e'], 'a/b/c/d/e' => '. in middle'],
[['0', 'a/b'], '0/a/b' => '0 at start'],
[['/0', 'a/b'], '/0/a/b' => '/0 at start'],
[['a/b/c', '0', 'd/e'], 'a/b/c/0/d/e' => '0 in middle'],
[['a/b', '0'], 'a/b/0' => '0 at end'],
);
for my $test (@TESTS) {
my ($inputs, $expected, $scenario) = @$test;
my $got = join_paths(@$inputs);
my $descr = "$scenario: in=[" . join(', ', map "'$_'", @$inputs) . "] exp=[$expected] got=[$got]";
is($got, $expected, $descr);
}

88
t/link_dest_within_stow_dir.t Executable file
View file

@ -0,0 +1,88 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing Stow::link_dest_within_stow_dir()
#
use strict;
use warnings;
use Test::More tests => 6;
use testutil;
use Stow::Util;
init_test_dirs();
# This is a relative path, unlike $ABS_TEST_DIR below.
my $stow = new_Stow(dir => "$TEST_DIR/stow",
target => "$TEST_DIR/target");
subtest("relative stow dir, link to top-level package file" => sub {
plan tests => 2;
my ($package, $path) =
$stow->link_dest_within_stow_dir("../stow/pkg/dir/file");
is($package, "pkg", "package");
is($path, "dir/file", "path");
});
subtest("relative stow dir, link to second-level package file" => sub {
plan tests => 2;
my ($package, $path) =
$stow->link_dest_within_stow_dir("../stow/pkg/dir/subdir/file");
is($package, "pkg", "package");
is($path, "dir/subdir/file", "path");
});
# This is an absolute path, unlike $TEST_DIR above.
$stow = new_Stow(dir => "$ABS_TEST_DIR/stow",
target => "$ABS_TEST_DIR/target");
subtest("relative stow dir, link to second-level package file" => sub {
plan tests => 2;
my ($package, $path) =
$stow->link_dest_within_stow_dir("../stow/pkg/dir/file");
is($package, "pkg", "package");
is($path, "dir/file", "path");
});
subtest("absolute stow dir, link to top-level package file" => sub {
plan tests => 2;
my ($package, $path) =
$stow->link_dest_within_stow_dir("../stow/pkg/dir/subdir/file");
is($package, "pkg", "package");
is($path, "dir/subdir/file", "path");
});
# Links with destination in the target are not pointing within
# the stow dir, so they're not owned by stow.
subtest("link to path in target" => sub {
plan tests => 2;
my ($package, $path) =
$stow->link_dest_within_stow_dir("./alien");
is($path, "", "alien is in target, so path is empty");
is($package, "", "alien is in target, so package is empty");
});
subtest("link to path outside target and stow dir" => sub {
plan tests => 2;
my ($package, $path) =
$stow->link_dest_within_stow_dir("../alien");
is($path, "", "alien is outside, so path is empty");
is($package, "", "alien is outside, so package is empty");
});

58
t/parent.t Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Testing parent()
#
use strict;
use warnings;
use Stow::Util qw(parent);
use Test::More tests => 5;
is(
parent('a/b/c'),
'a/b'
=> 'no leading or trailing /'
);
is(
parent('/a/b/c'),
'/a/b'
=> 'leading /'
);
is(
parent('a/b/c/'),
'a/b'
=> 'trailing /'
);
is(
parent('/////a///b///c///'),
'/a/b'
=> 'multiple /'
);
is (
parent('a'),
''
=> 'empty parent'
);

266
t/rc_options.t Executable file
View file

@ -0,0 +1,266 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Test processing of stowrc file.
#
use strict;
use warnings;
use Test::More tests => 34;
use testutil;
require 'stow';
# .stowrc files used for testing, relative to run_from/
my $CWD_RC_FILE = ".stowrc";
my $HOME_RC_FILE = "../.stowrc";
# Take the safe route and cowardly refuse to continue if there's
# already a file at $HOME_RC_FILE.
if (-e $HOME_RC_FILE) {
die "RC file location $HOME_RC_FILE already exists!\n";
}
my ($options, $pkgs_to_delete, $pkgs_to_stow);
# Init testing directory structure and overwrite ENV{HOME} to prevent
# squashing existing .stowrc file.
init_test_dirs();
# =========== RC Loading Tests ===========
# Basic parsing and loading rc file tests.
# ========================================
my $orig_HOME = $ENV{HOME};
#
# Test no .stowrc file anywhere
#
delete $ENV{HOME};
local @ARGV = ('dummy');
cd("$TEST_DIR/run_from");
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "$ABS_TEST_DIR", "default --target with no .stowrc");
is($options->{dir}, "$ABS_TEST_DIR/run_from", "default -d with no .stowrc");
#
# Test .stowrc file in cwd with relative paths, and $HOME not defined
#
make_file($CWD_RC_FILE, <<HERE);
-d ../stow
--target ../target
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "../target"
=> "relative --target from \$PWD/.stowrc");
is($options->{dir}, "../stow"
=> "relative -d from \$PWD/.stowrc");
$ENV{HOME} = $orig_HOME;
remove_file($CWD_RC_FILE);
#
# Test .stowrc file in cwd with absolute paths, and $HOME not defined
#
make_file($CWD_RC_FILE, <<HERE);
-d $ABS_TEST_DIR/stow
--target $ABS_TEST_DIR/target
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "$ABS_TEST_DIR/target"
=> "absolute --target from \$PWD/.stowrc");
is($options->{dir}, "$ABS_TEST_DIR/stow"
=> "abs_test_dir -d from \$PWD/.stowrc");
$ENV{HOME} = $orig_HOME;
remove_file($CWD_RC_FILE);
#
# Test ~/.stowrc file with one relative option per line.
#
local @ARGV = ('dummy');
make_file($HOME_RC_FILE, <<HERE);
-d ../stow
--target ../target
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "../target", "--target from \$HOME/.stowrc");
is($options->{dir}, "../stow", "-d from \$HOME/.stowrc");
#
# Test ~/.stowrc file with one absolute option per line.
#
local @ARGV = ('dummy');
make_file($HOME_RC_FILE, <<HERE);
-d $ABS_TEST_DIR/stow
--target $ABS_TEST_DIR/target
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "$ABS_TEST_DIR/target"
=> "--target from \$HOME/.stowrc");
is($options->{dir}, "$ABS_TEST_DIR/stow"
=> "-d from \$HOME/.stowrc");
#
# Test that some but not all options ~/.stowrc file are overridden by
# .stowrc in cwd.
#
local @ARGV = ('dummy');
make_file($HOME_RC_FILE, <<HERE);
-d $ABS_TEST_DIR/stow-will-be-overridden
--target $ABS_TEST_DIR/target-will-be-overridden
--defer=info
HERE
make_file($CWD_RC_FILE, <<HERE);
-d $ABS_TEST_DIR/stow
--target $ABS_TEST_DIR/target
--defer=man
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{target}, "$ABS_TEST_DIR/target"
=> "--target overridden by \$PWD/.stowrc");
is($options->{dir}, "$ABS_TEST_DIR/stow"
=> "-d overridden \$PWD/.stowrc");
is_deeply($options->{defer}, [qr(\Ainfo), qr(\Aman)],
'defer man and info');
unlink($CWD_RC_FILE) or die "Failed to unlink $CWD_RC_FILE";
#
# Test that scalar cli option overwrites conflicting ~/.stowrc option.
#
local @ARGV = ('-d', "$ABS_TEST_DIR/stow", 'dummy');
make_file($HOME_RC_FILE, <<HERE);
-d bad/path
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is($options->{dir}, "$ABS_TEST_DIR/stow", "cli overwrite scalar rc option.");
#
# Test that list cli option merges with conflicting .stowrc option.
# Documentation states that .stowrc options are prepended to cli options.
#
local @ARGV = (
'--defer=man',
'dummy'
);
make_file($HOME_RC_FILE, <<HERE);
--defer=info
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = process_options();
is_deeply($options->{defer}, [qr(\Ainfo), qr(\Aman)],
'defer man and info');
# ======== Filepath Expansion Tests ========
# Test proper filepath expansion in rc file.
# Expansion is only applied to options that
# take a filepath, namely target and dir.
# ==========================================
#
# Test environment variable expansion function.
#
# Basic expansion
is(expand_environment('$HOME/stow'), "$ABS_TEST_DIR/stow", 'expand $HOME');
is(expand_environment('${HOME}/stow'), "$ABS_TEST_DIR/stow", 'expand ${HOME}');
delete $ENV{UNDEFINED}; # just in case
foreach my $var ('$UNDEFINED', '${UNDEFINED}') {
eval {
expand_environment($var, "--foo option");
};
is(
$@,
"--foo option references undefined environment variable \$UNDEFINED; " .
"aborting!\n",
"expand $var"
);
}
# Expansion with an underscore.
$ENV{'WITH_UNDERSCORE'} = 'test string';
is(expand_environment('${WITH_UNDERSCORE}'), 'test string',
'expand ${WITH_UNDERSCORE}');
delete $ENV{'WITH_UNDERSCORE'};
# Expansion with escaped $
is(expand_environment('\$HOME/stow'), '$HOME/stow', 'expand \$HOME');
#
# Test tilde (~) expansion
#
# Basic expansion
is(expand_tilde('~/path'), "$ENV{HOME}/path", 'tilde expansion to $HOME');
# Should not expand if middle of path
is(expand_tilde('/path/~/here'), '/path/~/here', 'middle ~ not expanded');
# Test escaped ~
is(expand_tilde('\~/path'), '~/path', 'escaped tilde');
#
# Test that environment variable expansion is applied.
#
make_file($HOME_RC_FILE, <<'HERE');
--dir=$HOME/stow
--target=$HOME/stow
--ignore=\$HOME
--defer=\$HOME
--override=\$HOME
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = get_config_file_options();
is($options->{dir}, "$ABS_TEST_DIR/stow",
"apply environment expansion on \$HOME/.stowrc --dir");
is($options->{target}, "$ABS_TEST_DIR/stow",
"apply environment expansion on \$HOME/.stowrc --target");
is_deeply($options->{ignore}, [qr(\$HOME\z)],
"environment expansion not applied on --ignore");
is_deeply($options->{defer}, [qr(\A\$HOME)],
"environment expansion not applied on --defer");
is_deeply($options->{override}, [qr(\A\$HOME)],
"environment expansion not applied on --override");
#
# Test that tilde expansion is applied in correct places.
#
make_file($HOME_RC_FILE, <<'HERE');
--dir=~/stow
--target=~/stow
--ignore=~/stow
--defer=~/stow
--override=~/stow
HERE
($options, $pkgs_to_delete, $pkgs_to_stow) = get_config_file_options();
is($options->{dir}, "$ABS_TEST_DIR/stow",
"apply tilde expansion on \$HOME/.stowrc --dir");
is($options->{target}, "$ABS_TEST_DIR/stow",
"apply tilde expansion on \$HOME/.stowrc --target");
is_deeply($options->{ignore}, [qr(~/stow\z)],
"tilde expansion not applied on --ignore");
is_deeply($options->{defer}, [qr(\A~/stow)],
"tilde expansion not applied on --defer");
is_deeply($options->{override}, [qr(\A~/stow)],
"tilde expansion not applied on --override");
#
# Clean up files used for testing.
#
unlink $HOME_RC_FILE or die "Unable to clean up $HOME_RC_FILE.\n";
remove_dir($ABS_TEST_DIR);

571
t/stow.t Executable file
View file

@ -0,0 +1,571 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Test stowing packages.
#
use strict;
use warnings;
use Test::More tests => 22;
use Test::Output;
use English qw(-no_match_vars);
use Stow::Util qw(canon_path set_debug_level);
use testutil;
init_test_dirs();
cd("$TEST_DIR/target");
my $stow;
my %conflicts;
# Note that each of the following tests use a distinct set of files
subtest('stow a simple tree minimally', sub {
plan tests => 2;
my $stow = new_Stow(dir => '../stow');
make_path('../stow/pkg1/bin1');
make_file('../stow/pkg1/bin1/file1');
$stow->plan_stow('pkg1');
$stow->process_tasks();
is_deeply([ $stow->get_conflicts ], [], 'no conflicts with minimal stow');
is(
readlink('bin1'),
'../stow/pkg1/bin1',
=> 'minimal stow of a simple tree'
);
});
subtest('stow a simple tree into an existing directory', sub {
plan tests => 1;
my $stow = new_Stow();
make_path('../stow/pkg2/lib2');
make_file('../stow/pkg2/lib2/file2');
make_path('lib2');
$stow->plan_stow('pkg2');
$stow->process_tasks();
is(
readlink('lib2/file2'),
'../../stow/pkg2/lib2/file2',
=> 'stow simple tree to existing directory'
);
});
subtest('unfold existing tree', sub {
plan tests => 3;
my $stow = new_Stow();
make_path('../stow/pkg3a/bin3');
make_file('../stow/pkg3a/bin3/file3a');
make_link('bin3' => '../stow/pkg3a/bin3'); # emulate stow
make_path('../stow/pkg3b/bin3');
make_file('../stow/pkg3b/bin3/file3b');
$stow->plan_stow('pkg3b');
$stow->process_tasks();
ok(-d 'bin3');
is(readlink('bin3/file3a'), '../../stow/pkg3a/bin3/file3a');
is(readlink('bin3/file3b'), '../../stow/pkg3b/bin3/file3b'
=> 'target already has 1 stowed package');
});
subtest("Package dir 'bin4' conflicts with existing non-dir so can't unfold", sub {
plan tests => 2;
my $stow = new_Stow();
make_file('bin4'); # this is a file but named like a directory
make_path('../stow/pkg4/bin4');
make_file('../stow/pkg4/bin4/file4');
$stow->plan_stow('pkg4');
%conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 1);
like(
$conflicts{stow}{pkg4}[0],
qr!cannot stow ../stow/pkg4/bin4 over existing target bin4 since neither a link nor a directory and --adopt not specified!
=> 'link to new dir bin4 conflicts with existing non-directory'
);
});
subtest("Package dir 'bin4a' conflicts with existing non-dir " .
"so can't unfold even with --adopt", sub {
plan tests => 2;
my $stow = new_Stow(adopt => 1);
make_file('bin4a'); # this is a file but named like a directory
make_path('../stow/pkg4a/bin4a');
make_file('../stow/pkg4a/bin4a/file4a');
$stow->plan_stow('pkg4a');
%conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 1);
like(
$conflicts{stow}{pkg4a}[0],
qr!cannot stow directory ../stow/pkg4a/bin4a over existing non-directory target bin4a!
=> 'link to new dir bin4a conflicts with existing non-directory'
);
});
subtest("Package files 'file4b' and 'bin4b' conflict with existing files", sub {
plan tests => 3;
my $stow = new_Stow();
# Populate target
make_file('file4b', 'file4b - version originally in target');
make_path('bin4b');
make_file('bin4b/file4b', 'bin4b/file4b - version originally in target');
# Populate stow package
make_path('../stow/pkg4b');
make_file('../stow/pkg4b/file4b', 'file4b - version originally in stow package');
make_path('../stow/pkg4b/bin4b');
make_file('../stow/pkg4b/bin4b/file4b', 'bin4b/file4b - version originally in stow package');
$stow->plan_stow('pkg4b');
%conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 2 => 'conflict per file');
for my $i (0, 1) {
my $target = $i ? 'file4b' : 'bin4b/file4b';
like(
$conflicts{stow}{pkg4b}[$i],
qr,cannot stow ../stow/pkg4b/$target over existing target $target since neither a link nor a directory and --adopt not specified,
=> 'link to file4b conflicts with existing non-directory'
);
}
});
subtest("Package files 'file4d' conflicts with existing directories", sub {
plan tests => 3;
my $stow = new_Stow();
# Populate target
make_path('file4d'); # this is a directory but named like a file to create the conflict
make_path('bin4d/file4d'); # same here
# Populate stow package
make_path('../stow/pkg4d');
make_file('../stow/pkg4d/file4d', 'file4d - version originally in stow package');
make_path('../stow/pkg4d/bin4d');
make_file('../stow/pkg4d/bin4d/file4d', 'bin4d/file4d - version originally in stow package');
$stow->plan_stow('pkg4d');
%conflicts = $stow->get_conflicts();
is($stow->get_conflict_count, 2 => 'conflict per file');
for my $i (0, 1) {
my $target = $i ? 'file4d' : 'bin4d/file4d';
like(
$conflicts{stow}{pkg4d}[$i],
qr!cannot stow non-directory ../stow/pkg4d/$target over existing directory target $target!
=> 'link to file4d conflicts with existing non-directory'
);
}
});
subtest("Package files 'file4c' and 'bin4c' can adopt existing versions", sub {
plan tests => 8;
my $stow = new_Stow(adopt => 1);
# Populate target
make_file('file4c', "file4c - version originally in target\n");
make_path ('bin4c');
make_file('bin4c/file4c', "bin4c/file4c - version originally in target\n");
# Populate stow package
make_path('../stow/pkg4c');
make_file('../stow/pkg4c/file4c', "file4c - version originally in stow package\n");
make_path ('../stow/pkg4c/bin4c');
make_file('../stow/pkg4c/bin4c/file4c', "bin4c/file4c - version originally in stow package\n");
$stow->plan_stow('pkg4c');
is($stow->get_conflict_count, 0 => 'no conflicts with --adopt');
is($stow->get_tasks, 4 => 'two tasks per file');
$stow->process_tasks();
for my $file ('file4c', 'bin4c/file4c') {
ok(-l $file, "$file turned into a symlink");
is(
readlink $file,
(index($file, '/') == -1 ? '' : '../' )
. "../stow/pkg4c/$file" => "$file points to right place"
);
is(cat_file($file), "$file - version originally in target\n" => "$file has right contents");
}
});
subtest("Target already exists but is not owned by stow", sub {
plan tests => 1;
my $stow = new_Stow();
make_path('bin5');
make_invalid_link('bin5/file5','../../empty');
make_path('../stow/pkg5/bin5/file5');
$stow->plan_stow('pkg5');
%conflicts = $stow->get_conflicts();
like(
$conflicts{stow}{pkg5}[-1],
qr/not owned by stow/
=> 'target already exists but is not owned by stow'
);
});
subtest("Replace existing but invalid target", sub {
plan tests => 1;
my $stow = new_Stow();
make_invalid_link('file6','../stow/path-does-not-exist');
make_path('../stow/pkg6');
make_file('../stow/pkg6/file6');
$stow->plan_stow('pkg6');
$stow->process_tasks();
is(
readlink('file6'),
'../stow/pkg6/file6'
=> 'replace existing but invalid target'
);
});
subtest("Target already exists, is owned by stow, but points to a non-directory", sub {
plan tests => 1;
my $stow = new_Stow();
#set_debug_level(4);
make_path('bin7');
make_path('../stow/pkg7a/bin7');
make_file('../stow/pkg7a/bin7/node7');
make_link('bin7/node7','../../stow/pkg7a/bin7/node7');
make_path('../stow/pkg7b/bin7/node7');
make_file('../stow/pkg7b/bin7/node7/file7');
$stow->plan_stow('pkg7b');
%conflicts = $stow->get_conflicts();
like(
$conflicts{stow}{pkg7b}[-1],
qr/existing target is stowed to a different package/
=> 'link to new dir conflicts with existing stowed non-directory'
);
});
subtest("stowing directories named 0", sub {
plan tests => 4;
my $stow = new_Stow();
make_path('../stow/pkg8a/0');
make_file('../stow/pkg8a/0/file8a');
make_link('0' => '../stow/pkg8a/0'); # emulate stow
make_path('../stow/pkg8b/0');
make_file('../stow/pkg8b/0/file8b');
$stow->plan_stow('pkg8b');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
ok(-d '0');
is(readlink('0/file8a'), '../../stow/pkg8a/0/file8a');
is(readlink('0/file8b'), '../../stow/pkg8b/0/file8b'
=> 'stowing directories named 0'
);
});
subtest("overriding already stowed documentation", sub {
plan tests => 2;
my $stow = new_Stow(override => ['man9', 'info9']);
make_path('../stow/pkg9a/man9/man1');
make_file('../stow/pkg9a/man9/man1/file9.1');
make_path('man9/man1');
make_link('man9/man1/file9.1' => '../../../stow/pkg9a/man9/man1/file9.1'); # emulate stow
make_path('../stow/pkg9b/man9/man1');
make_file('../stow/pkg9b/man9/man1/file9.1');
$stow->plan_stow('pkg9b');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
is(readlink('man9/man1/file9.1'), '../../../stow/pkg9b/man9/man1/file9.1'
=> 'overriding existing documentation files'
);
});
subtest("deferring to already stowed documentation", sub {
plan tests => 3;
my $stow = new_Stow(defer => ['man10', 'info10']);
make_path('../stow/pkg10a/man10/man1');
make_file('../stow/pkg10a/man10/man1/file10.1');
make_path('man10/man1');
make_link('man10/man1/file10.1' => '../../../stow/pkg10a/man10/man1/file10.1'); # emulate stow
make_path('../stow/pkg10b/man10/man1');
make_file('../stow/pkg10b/man10/man1/file10.1');
$stow->plan_stow('pkg10b');
is($stow->get_tasks, 0, 'no tasks to process');
is($stow->get_conflict_count, 0);
is(readlink('man10/man1/file10.1'), '../../../stow/pkg10a/man10/man1/file10.1'
=> 'defer to existing documentation files'
);
});
subtest("Ignore temp files", sub {
plan tests => 4;
my $stow = new_Stow(ignore => ['~', '\.#.*']);
make_path('../stow/pkg11/man11/man1');
make_file('../stow/pkg11/man11/man1/file11.1');
make_file('../stow/pkg11/man11/man1/file11.1~');
make_file('../stow/pkg11/man11/man1/.#file11.1');
make_path('man11/man1');
$stow->plan_stow('pkg11');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
is(readlink('man11/man1/file11.1'), '../../../stow/pkg11/man11/man1/file11.1');
ok(!-e 'man11/man1/file11.1~');
ok(!-e 'man11/man1/.#file11.1'
=> 'ignore temp files'
);
});
subtest("stowing links library files", sub {
plan tests => 3;
my $stow = new_Stow();
make_path('../stow/pkg12/lib12/');
make_file('../stow/pkg12/lib12/lib.so.1');
make_link('../stow/pkg12/lib12/lib.so', 'lib.so.1');
make_path('lib12/');
$stow->plan_stow('pkg12');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
is(readlink('lib12/lib.so.1'), '../../stow/pkg12/lib12/lib.so.1');
is(readlink('lib12/lib.so'), '../../stow/pkg12/lib12/lib.so'
=> 'stow links to libraries'
);
});
subtest("unfolding to stow links to library files", sub {
plan tests => 5;
my $stow = new_Stow();
make_path('../stow/pkg13a/lib13/');
make_file('../stow/pkg13a/lib13/liba.so.1');
make_link('../stow/pkg13a/lib13/liba.so', 'liba.so.1');
make_link('lib13','../stow/pkg13a/lib13');
make_path('../stow/pkg13b/lib13/');
make_file('../stow/pkg13b/lib13/libb.so.1');
make_link('../stow/pkg13b/lib13/libb.so', 'libb.so.1');
$stow->plan_stow('pkg13b');
$stow->process_tasks();
is($stow->get_conflict_count, 0);
is(readlink('lib13/liba.so.1'), '../../stow/pkg13a/lib13/liba.so.1');
is(readlink('lib13/liba.so' ), '../../stow/pkg13a/lib13/liba.so');
is(readlink('lib13/libb.so.1'), '../../stow/pkg13b/lib13/libb.so.1');
is(readlink('lib13/libb.so' ), '../../stow/pkg13b/lib13/libb.so'
=> 'unfolding to stow links to libraries'
);
});
subtest("stowing to stow dir should fail", sub {
plan tests => 4;
make_path('stow');
$stow = new_Stow(dir => 'stow');
make_path('stow/pkg14/stow/pkg15');
make_file('stow/pkg14/stow/pkg15/node15');
stderr_like(
sub { $stow->plan_stow('pkg14'); },
qr/WARNING: skipping target which was current stow directory stow/,
"stowing to stow dir should give warning"
);
is($stow->get_tasks, 0, 'no tasks to process');
is($stow->get_conflict_count, 0);
ok(
! -l 'stow/pkg15'
=> "stowing to stow dir should fail"
);
});
subtest("stow a simple tree minimally when cwd isn't target", sub {
plan tests => 2;
cd('../..');
$stow = new_Stow(dir => "$TEST_DIR/stow", target => "$TEST_DIR/target");
make_path("$TEST_DIR/stow/pkg16/bin16");
make_file("$TEST_DIR/stow/pkg16/bin16/file16");
$stow->plan_stow('pkg16');
$stow->process_tasks();
is_deeply([ $stow->get_conflicts ], [], 'no conflicts with minimal stow');
is(
readlink("$TEST_DIR/target/bin16"),
'../stow/pkg16/bin16',
=> "minimal stow of a simple tree when cwd isn't target"
);
});
subtest("stow a simple tree minimally to absolute stow dir when cwd isn't", sub {
plan tests => 2;
my $stow = new_Stow(dir => canon_path("$TEST_DIR/stow"),
target => "$TEST_DIR/target");
make_path("$TEST_DIR/stow/pkg17/bin17");
make_file("$TEST_DIR/stow/pkg17/bin17/file17");
$stow->plan_stow('pkg17');
$stow->process_tasks();
is_deeply([ $stow->get_conflicts ], [], 'no conflicts with minimal stow');
is(
readlink("$TEST_DIR/target/bin17"),
'../stow/pkg17/bin17',
=> "minimal stow of a simple tree with absolute stow dir"
);
});
subtest("stow a simple tree minimally with absolute stow AND target dirs when", sub {
plan tests => 2;
my $stow = new_Stow(dir => canon_path("$TEST_DIR/stow"),
target => canon_path("$TEST_DIR/target"));
make_path("$TEST_DIR/stow/pkg18/bin18");
make_file("$TEST_DIR/stow/pkg18/bin18/file18");
$stow->plan_stow('pkg18');
$stow->process_tasks();
is_deeply([ $stow->get_conflicts ], [], 'no conflicts with minimal stow');
is(
readlink("$TEST_DIR/target/bin18"),
'../stow/pkg18/bin18',
=> "minimal stow of a simple tree with absolute stow and target dirs"
);
});
subtest("stow a tree with no-folding enabled", sub {
plan tests => 82;
# folded directories should be split open (unfolded) where
# (and only where) necessary
#
cd("$TEST_DIR/target");
sub create_pkg {
my ($id, $pkg) = @_;
my $stow_pkg = "../stow/$id-$pkg";
make_path ($stow_pkg);
make_file("$stow_pkg/$id-file-$pkg");
# create a shallow hierarchy specific to this package which isn't
# yet stowed
make_path ("$stow_pkg/$id-$pkg-only-new");
make_file("$stow_pkg/$id-$pkg-only-new/$id-file-$pkg");
# create a deeper hierarchy specific to this package which isn't
# yet stowed
make_path ("$stow_pkg/$id-$pkg-only-new2/subdir");
make_file("$stow_pkg/$id-$pkg-only-new2/subdir/$id-file-$pkg");
make_link("$stow_pkg/$id-$pkg-only-new2/current", "subdir");
# create a hierarchy specific to this package which is already
# stowed via a folded tree
make_path ("$stow_pkg/$id-$pkg-only-old");
make_link("$id-$pkg-only-old", "$stow_pkg/$id-$pkg-only-old");
make_file("$stow_pkg/$id-$pkg-only-old/$id-file-$pkg");
# create a shared hierarchy which this package uses
make_path ("$stow_pkg/$id-shared");
make_file("$stow_pkg/$id-shared/$id-file-$pkg");
# create a partially shared hierarchy which this package uses
make_path ("$stow_pkg/$id-shared2/subdir-$pkg");
make_file("$stow_pkg/$id-shared2/$id-file-$pkg");
make_file("$stow_pkg/$id-shared2/subdir-$pkg/$id-file-$pkg");
}
foreach my $pkg (qw{a b}) {
create_pkg('no-folding', $pkg);
}
$stow = new_Stow('no-folding' => 1);
$stow->plan_stow('no-folding-a');
is_deeply([ $stow->get_conflicts ], [] => 'no conflicts with --no-folding');
my @tasks = $stow->get_tasks;
use Data::Dumper;
is(scalar(@tasks), 13 => "6 dirs, 7 links") || warn Dumper(\@tasks);
$stow->process_tasks();
sub check_no_folding {
my ($pkg) = @_;
my $stow_pkg = "../stow/no-folding-$pkg";
is_link("no-folding-file-$pkg", "$stow_pkg/no-folding-file-$pkg");
# check existing folded tree is untouched
is_link("no-folding-$pkg-only-old", "$stow_pkg/no-folding-$pkg-only-old");
# check newly stowed shallow tree is not folded
is_dir_not_symlink("no-folding-$pkg-only-new");
is_link("no-folding-$pkg-only-new/no-folding-file-$pkg",
"../$stow_pkg/no-folding-$pkg-only-new/no-folding-file-$pkg");
# check newly stowed deeper tree is not folded
is_dir_not_symlink("no-folding-$pkg-only-new2");
is_dir_not_symlink("no-folding-$pkg-only-new2/subdir");
is_link("no-folding-$pkg-only-new2/subdir/no-folding-file-$pkg",
"../../$stow_pkg/no-folding-$pkg-only-new2/subdir/no-folding-file-$pkg");
is_link("no-folding-$pkg-only-new2/current",
"../$stow_pkg/no-folding-$pkg-only-new2/current");
# check shared tree is not folded. first time round this will be
# newly stowed.
is_dir_not_symlink('no-folding-shared');
is_link("no-folding-shared/no-folding-file-$pkg",
"../$stow_pkg/no-folding-shared/no-folding-file-$pkg");
# check partially shared tree is not folded. first time round this
# will be newly stowed.
is_dir_not_symlink('no-folding-shared2');
is_link("no-folding-shared2/no-folding-file-$pkg",
"../$stow_pkg/no-folding-shared2/no-folding-file-$pkg");
is_link("no-folding-shared2/no-folding-file-$pkg",
"../$stow_pkg/no-folding-shared2/no-folding-file-$pkg");
}
check_no_folding('a');
$stow = new_Stow('no-folding' => 1);
$stow->plan_stow('no-folding-b');
is_deeply([ $stow->get_conflicts ], [] => 'no conflicts with --no-folding');
@tasks = $stow->get_tasks;
is(scalar(@tasks), 11 => '4 dirs, 7 links') || warn Dumper(\@tasks);
$stow->process_tasks();
check_no_folding('a');
check_no_folding('b');
});

307
t/testutil.pm Executable file
View file

@ -0,0 +1,307 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Utilities shared by test scripts
#
package testutil;
use strict;
use warnings;
use Carp qw(confess croak);
use File::Basename;
use File::Path qw(make_path remove_tree);
use File::Spec;
use Test::More;
use Stow;
use Stow::Util qw(parent canon_path);
use base qw(Exporter);
our @EXPORT = qw(
$ABS_TEST_DIR
$TEST_DIR
init_test_dirs
cd
new_Stow new_compat_Stow
make_path make_link make_invalid_link make_file
remove_dir remove_file remove_link
cat_file
is_link is_dir_not_symlink is_nonexistent_path
);
our $TEST_DIR = 'tmp-testing-trees';
our $ABS_TEST_DIR = File::Spec->rel2abs('tmp-testing-trees');
sub init_test_dirs {
my $test_dir = shift || $TEST_DIR;
my $abs_test_dir = File::Spec->rel2abs($test_dir);
# Create a run_from/ subdirectory for tests which want to run
# from a separate directory outside the Stow directory or
# target directory.
for my $dir ("target", "stow", "run_from") {
my $path = "$test_dir/$dir";
-d $path and remove_tree($path);
make_path($path);
}
# Don't let user's ~/.stow-global-ignore affect test results
$ENV{HOME} = $abs_test_dir;
return $abs_test_dir;
}
sub new_Stow {
my %opts = @_;
# These default paths assume that execution will be triggered from
# within the target directory.
$opts{dir} ||= '../stow';
$opts{target} ||= '.';
$opts{test_mode} = 1;
my $stow = eval { new Stow(%opts) };
if ($@) {
confess "Error while trying to instantiate new Stow(%opts): $@";
}
return $stow;
}
sub new_compat_Stow {
my %opts = @_;
$opts{compat} = 1;
return new_Stow(%opts);
}
#===== SUBROUTINE ===========================================================
# Name : make_link()
# Purpose : safely create a link
# Parameters: $link_src => path to the link
# : $link_dest => where the new link should point
# : $invalid => true iff $link_dest refers to non-existent file
# Returns : n/a
# Throws : fatal error if the link can not be safely created
# Comments : checks for existing nodes
#============================================================================
sub make_link {
my ($link_src, $link_dest, $invalid) = @_;
if (-l $link_src) {
my $old_source = readlink join('/', parent($link_src), $link_dest)
or croak "$link_src is already a link but could not read link $link_src/$link_dest";
if ($old_source ne $link_dest) {
croak "$link_src already exists but points elsewhere\n";
}
}
croak "$link_src already exists and is not a link\n" if -e $link_src;
my $abs_target = File::Spec->rel2abs($link_src);
my $link_src_container = dirname($abs_target);
my $abs_source = File::Spec->rel2abs($link_dest, $link_src_container);
#warn "t $link_src c $link_src_container as $abs_source";
if (-e $abs_source) {
croak "Won't make invalid link pointing to existing $abs_target"
if $invalid;
}
else {
croak "Won't make link pointing to non-existent $abs_target"
unless $invalid;
}
symlink $link_dest, $link_src
or croak "could not create link $link_src => $link_dest ($!)\n";
}
#===== SUBROUTINE ===========================================================
# Name : make_invalid_link()
# Purpose : safely create an invalid link
# Parameters: $target => path to the link
# : $source => the non-existent source where the new link should point
# Returns : n/a
# Throws : fatal error if the link can not be safely created
# Comments : checks for existing nodes
#============================================================================
sub make_invalid_link {
my ($target, $source, $allow_invalid) = @_;
make_link($target, $source, 1);
}
#===== SUBROUTINE ===========================================================
# Name : create_file()
# Purpose : create an empty file
# Parameters: $path => proposed path to the file
# : $contents => (optional) contents to write to file
# Returns : n/a
# Throws : fatal error if the file could not be created
# Comments : detects clash with an existing non-file
#============================================================================
sub make_file {
my ($path, $contents) = @_;
if (-e $path and ! -f $path) {
croak "a non-file already exists at $path\n";
}
open my $FILE ,'>', $path
or croak "could not create file: $path ($!)\n";
print $FILE $contents if defined $contents;
close $FILE;
}
#===== SUBROUTINE ===========================================================
# Name : remove_link()
# Purpose : remove an esiting symbolic link
# Parameters: $path => path to the symbolic link
# Returns : n/a
# Throws : fatal error if the operation fails or if passed the path to a
# : non-link
# Comments : none
#============================================================================
sub remove_link {
my ($path) = @_;
if (not -l $path) {
croak qq(remove_link() called with a non-link: $path);
}
unlink $path or croak "could not remove link: $path ($!)\n";
return;
}
#===== SUBROUTINE ===========================================================
# Name : remove_file()
# Purpose : remove an existing empty file
# Parameters: $path => the path to the empty file
# Returns : n/a
# Throws : fatal error if given file is non-empty or the operation fails
# Comments : none
#============================================================================
sub remove_file {
my ($path) = @_;
if (-z $path) {
croak "file at $path is non-empty\n";
}
unlink $path or croak "could not remove empty file: $path ($!)\n";
return;
}
#===== SUBROUTINE ===========================================================
# Name : remove_dir()
# Purpose : safely remove a tree of test files
# Parameters: $dir => path to the top of the tree
# Returns : n/a
# Throws : fatal error if the tree contains a non-link or non-empty file
# Comments : recursively removes directories containing softlinks empty files
#============================================================================
sub remove_dir {
my ($dir) = @_;
if (not -d $dir) {
croak "$dir is not a directory";
}
opendir my $DIR, $dir or croak "cannot read directory: $dir ($!)\n";
my @listing = readdir $DIR;
closedir $DIR;
NODE:
for my $node (@listing) {
next NODE if $node eq '.';
next NODE if $node eq '..';
my $path = "$dir/$node";
if (-l $path or (-f $path and -z $path) or $node eq $Stow::LOCAL_IGNORE_FILE) {
unlink $path or croak "cannot unlink $path ($!)\n";
}
elsif (-d "$path") {
remove_dir($path);
}
else {
croak "$path is not a link, directory, or empty file\n";
}
}
rmdir $dir or croak "cannot rmdir $dir ($!)\n";
return;
}
#===== SUBROUTINE ===========================================================
# Name : cd()
# Purpose : wrapper around chdir
# Parameters: $dir => path to chdir to
# Returns : n/a
# Throws : fatal error if the chdir fails
# Comments : none
#============================================================================
sub cd {
my ($dir) = @_;
chdir $dir or croak "Failed to chdir($dir): $!\n";
}
#===== SUBROUTINE ===========================================================
# Name : cat_file()
# Purpose : return file contents
# Parameters: $file => file to read
# Returns : n/a
# Throws : fatal error if the open fails
# Comments : none
#============================================================================
sub cat_file {
my ($file) = @_;
open F, $file or croak "Failed to open($file): $!\n";
my $contents = join '', <F>;
close(F);
return $contents;
}
#===== SUBROUTINE ===========================================================
# Name : is_link()
# Purpose : assert path is a symlink
# Parameters: $path => path to check
# : $dest => target symlink should point to
#============================================================================
sub is_link {
my ($path, $dest) = @_;
ok(-l $path => "$path should be symlink");
is(readlink $path, $dest => "$path symlinks to $dest");
}
#===== SUBROUTINE ===========================================================
# Name : is_dir_not_symlink()
# Purpose : assert path is a directory not a symlink
# Parameters: $path => path to check
#============================================================================
sub is_dir_not_symlink {
my ($path) = @_;
ok(! -l $path => "$path should not be symlink");
ok(-d _ => "$path should be a directory");
}
#===== SUBROUTINE ===========================================================
# Name : is_nonexistent_path()
# Purpose : assert path does not exist
# Parameters: $path => path to check
#============================================================================
sub is_nonexistent_path {
my ($path) = @_;
ok(! -l $path => "$path should not be symlink");
ok(! -e _ => "$path should not exist");
}
1;
# Local variables:
# mode: perl
# end:
# vim: ft=perl

549
t/unstow.t Executable file
View file

@ -0,0 +1,549 @@
#!/usr/bin/perl
#
# This file is part of GNU Stow.
#
# GNU Stow is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU Stow is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
#
# Test unstowing packages
#
use strict;
use warnings;
use File::Spec qw(make_path);
use POSIX qw(getcwd);
use Test::More tests => 35;
use Test::Output;
use English qw(-no_match_vars);
use testutil;
use Stow::Util qw(canon_path);
my $repo = getcwd();
init_test_dirs($TEST_DIR);
our $COMPAT_TEST_DIR = "${TEST_DIR}-compat";
our $COMPAT_ABS_TEST_DIR = init_test_dirs($COMPAT_TEST_DIR);
sub init_stow2 {
make_path('stow2'); # make our alternate stow dir a subdir of target
make_file('stow2/.stow');
}
sub create_unowned_files {
# Make things harder for Stow to figure out, by adding
# a bunch of alien files unrelated to Stow.
my @UNOWNED_DIRS = ('unowned-dir', '.unowned-dir', 'dot-unowned-dir');
for my $dir ('.', @UNOWNED_DIRS) {
for my $subdir ('.', @UNOWNED_DIRS) {
make_path("$dir/$subdir");
make_file("$dir/$subdir/unowned");
make_file("$dir/$subdir/.unowned");
make_file("$dir/$subdir/dot-unowned");
}
}
}
# Run a subtest twice, with compat off then on, in parallel test trees.
#
# Params: $name[, $setup], $test_code
#
# $setup is an optional ref to an options hash to pass into the new
# Stow() constructor, or a ref to a sub which performs setup before
# the constructor gets called and then returns that options hash.
sub subtests {
my $name = shift;
my $setup = @_ == 2 ? shift : {};
my $code = shift;
$ENV{HOME} = $ABS_TEST_DIR;
cd($repo);
cd("$TEST_DIR/target");
create_unowned_files();
# cd first to allow setup to cd somewhere else.
my $opts = ref($setup) eq 'HASH' ? $setup : $setup->($TEST_DIR);
subtest($name, sub {
make_path($opts->{dir}) if $opts->{dir};
my $stow = new_Stow(%$opts);
$code->($stow, $TEST_DIR);
});
$ENV{HOME} = $COMPAT_ABS_TEST_DIR;
cd($repo);
cd("$COMPAT_TEST_DIR/target");
create_unowned_files();
# cd first to allow setup to cd somewhere else.
$opts = ref $setup eq 'HASH' ? $setup : $setup->($COMPAT_TEST_DIR);
subtest("$name (compat mode)", sub {
make_path($opts->{dir}) if $opts->{dir};
my $stow = new_compat_Stow(%$opts);
$code->($stow, $COMPAT_TEST_DIR);
});
}
sub plan_tests {
my ($stow, $count) = @_;
plan tests => $stow->{compat} ? $count + 2 : $count;
}
subtests("unstow a simple tree minimally", sub {
my ($stow) = @_;
plan tests => 3;
make_path('../stow/pkg1/bin1');
make_file('../stow/pkg1/bin1/file1');
make_link('bin1', '../stow/pkg1/bin1');
$stow->plan_unstow('pkg1');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(-f '../stow/pkg1/bin1/file1');
ok(! -e 'bin1' => 'unstow a simple tree');
});
subtests("unstow a simple tree from an existing directory", sub {
my ($stow) = @_;
plan tests => 3;
make_path('lib2');
make_path('../stow/pkg2/lib2');
make_file('../stow/pkg2/lib2/file2');
make_link('lib2/file2', '../../stow/pkg2/lib2/file2');
$stow->plan_unstow('pkg2');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(-f '../stow/pkg2/lib2/file2');
ok(-d 'lib2'
=> 'unstow simple tree from a pre-existing directory'
);
});
subtests("fold tree after unstowing", sub {
my ($stow) = @_;
plan tests => 3;
make_path('bin3');
make_path('../stow/pkg3a/bin3');
make_file('../stow/pkg3a/bin3/file3a');
make_link('bin3/file3a' => '../../stow/pkg3a/bin3/file3a'); # emulate stow
make_path('../stow/pkg3b/bin3');
make_file('../stow/pkg3b/bin3/file3b');
make_link('bin3/file3b' => '../../stow/pkg3b/bin3/file3b'); # emulate stow
$stow->plan_unstow('pkg3b');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(-l 'bin3');
is(readlink('bin3'), '../stow/pkg3a/bin3'
=> 'fold tree after unstowing'
);
});
subtests("existing link is owned by stow but is invalid so it gets removed anyway", sub {
my ($stow) = @_;
plan tests => 2;
make_path('bin4');
make_path('../stow/pkg4/bin4');
make_file('../stow/pkg4/bin4/file4');
make_invalid_link('bin4/file4', '../../stow/pkg4/bin4/does-not-exist');
$stow->plan_unstow('pkg4');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(! -e 'bin4/file4'
=> q(remove invalid link owned by stow)
);
});
subtests("Existing invalid link is not owned by stow", sub {
my ($stow) = @_;
plan tests => 3;
make_path('../stow/pkg5/bin5');
make_invalid_link('bin5', '../not-stow');
$stow->plan_unstow('pkg5');
is($stow->get_conflict_count, 0, 'conflict count');
ok(-l 'bin5', 'invalid link not removed');
is(readlink('bin5'), '../not-stow' => "invalid link not changed");
});
subtests("Target already exists, is owned by stow, but points to a different package", sub {
my ($stow) = @_;
plan tests => 3;
make_path('bin6');
make_path('../stow/pkg6a/bin6');
make_file('../stow/pkg6a/bin6/file6');
make_link('bin6/file6', '../../stow/pkg6a/bin6/file6');
make_path('../stow/pkg6b/bin6');
make_file('../stow/pkg6b/bin6/file6');
$stow->plan_unstow('pkg6b');
is($stow->get_conflict_count, 0, 'conflict count');
ok(-l 'bin6/file6');
is(
readlink('bin6/file6'),
'../../stow/pkg6a/bin6/file6'
=> q(ignore existing link that points to a different package)
);
});
subtests("Don't unlink anything under the stow directory",
sub {
make_path('stow');
return { dir => 'stow' };
# target dir defaults to parent of stow, which is target directory
},
sub {
plan tests => 5;
my ($stow) = @_;
# Emulate stowing into ourself (bizarre corner case or accident):
make_path('stow/pkg7a/stow/pkg7b');
make_file('stow/pkg7a/stow/pkg7b/file7b');
# Make a package be a link to a package of the same name inside another package.
make_link('stow/pkg7b', '../stow/pkg7a/stow/pkg7b');
stderr_like(
sub { $stow->plan_unstow('pkg7b'); },
$stow->{compat} ? qr/WARNING: skipping target which was current stow directory stow/ : qr//
=> "warn when unstowing from ourself"
);
is($stow->get_tasks, 0, 'no tasks to process when unstowing pkg7b');
is($stow->get_conflict_count, 0, 'conflict count');
ok(-l 'stow/pkg7b');
is(
readlink('stow/pkg7b'),
'../stow/pkg7a/stow/pkg7b'
=> q(don't unlink any nodes under the stow directory)
);
});
subtests("Don't unlink any nodes under another stow directory",
sub {
make_path('stow');
return { dir => 'stow' };
},
sub {
my ($stow) = @_;
plan tests => 5;
init_stow2();
# emulate stowing into ourself (bizarre corner case or accident)
make_path('stow/pkg8a/stow2/pkg8b');
make_file('stow/pkg8a/stow2/pkg8b/file8b');
make_link('stow2/pkg8b', '../stow/pkg8a/stow2/pkg8b');
stderr_like(
sub { $stow->plan_unstow('pkg8a'); },
qr/WARNING: skipping marked Stow directory stow2/
=> "warn when skipping unstowing"
);
is($stow->get_tasks, 0, 'no tasks to process when unstowing pkg8a');
is($stow->get_conflict_count, 0, 'conflict count');
ok(-l 'stow2/pkg8b');
is(
readlink('stow2/pkg8b'),
'../stow/pkg8a/stow2/pkg8b'
=> q(don't unlink any nodes under another stow directory)
);
});
# This will be used by subsequent tests
sub check_protected_dirs_skipped {
my ($stderr) = @_;
for my $dir (qw{stow stow2}) {
like($stderr,
qr/WARNING: skipping marked Stow directory $dir/
=> "warn when skipping marked directory $dir");
}
}
subtests("overriding already stowed documentation",
{override => ['man9', 'info9']},
sub {
my ($stow) = @_;
plan_tests($stow, 2);
make_file('stow/.stow');
init_stow2();
make_path('../stow/pkg9a/man9/man1');
make_file('../stow/pkg9a/man9/man1/file9.1');
make_path('man9/man1');
make_link('man9/man1/file9.1' => '../../../stow/pkg9a/man9/man1/file9.1'); # emulate stow
make_path('../stow/pkg9b/man9/man1');
make_file('../stow/pkg9b/man9/man1/file9.1');
my $stderr = stderr_from { $stow->plan_unstow('pkg9b') };
check_protected_dirs_skipped($stderr) if $stow->{compat};
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(!-l 'man9/man1/file9.1'
=> 'overriding existing documentation files'
);
});
subtests("deferring to already stowed documentation",
{defer => ['man10', 'info10']},
sub {
my ($stow) = @_;
plan_tests($stow, 3);
init_stow2();
make_path('../stow/pkg10a/man10/man1');
make_file('../stow/pkg10a/man10/man1/file10a.1');
make_path('man10/man1');
make_link('man10/man1/file10a.1' => '../../../stow/pkg10a/man10/man1/file10a.1');
# need this to block folding
make_path('../stow/pkg10b/man10/man1');
make_file('../stow/pkg10b/man10/man1/file10b.1');
make_link('man10/man1/file10b.1' => '../../../stow/pkg10b/man10/man1/file10b.1');
make_path('../stow/pkg10c/man10/man1');
make_file('../stow/pkg10c/man10/man1/file10a.1');
my $stderr = stderr_from { $stow->plan_unstow('pkg10c') };
check_protected_dirs_skipped($stderr) if $stow->{compat};
is($stow->get_tasks, 0, 'no tasks to process when unstowing pkg10c');
is($stow->get_conflict_count, 0, 'conflict count');
is(
readlink('man10/man1/file10a.1'),
'../../../stow/pkg10a/man10/man1/file10a.1'
=> 'defer to existing documentation files'
);
});
subtests("Ignore temp files",
{ignore => ['~', '\.#.*']},
sub {
my ($stow) = @_;
plan_tests($stow, 2);
init_stow2();
make_path('../stow/pkg12/man12/man1');
make_file('../stow/pkg12/man12/man1/file12.1');
make_file('../stow/pkg12/man12/man1/file12.1~');
make_file('../stow/pkg12/man12/man1/.#file12.1');
make_path('man12/man1');
make_link('man12/man1/file12.1' => '../../../stow/pkg12/man12/man1/file12.1');
my $stderr = stderr_from { $stow->plan_unstow('pkg12') };
check_protected_dirs_skipped($stderr) if $stow->{compat};
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(! -e 'man12/man1/file12.1' => 'man12/man1/file12.1 was unstowed');
});
subtests("Unstow an already unstowed package", sub {
my ($stow) = @_;
plan_tests($stow, 2);
my $stderr = stderr_from { $stow->plan_unstow('pkg12') };
check_protected_dirs_skipped($stderr) if $stow->{compat};
is($stow->get_tasks, 0, 'no tasks to process when unstowing pkg12');
is($stow->get_conflict_count, 0, 'conflict count');
});
subtests("Unstow a never stowed package", sub {
my ($stow) = @_;
plan tests => 2;
eval { remove_dir($stow->{target}); };
mkdir($stow->{target});
$stow->plan_unstow('pkg12');
is($stow->get_tasks, 0, 'no tasks to process when unstowing pkg12 which was never stowed');
is($stow->get_conflict_count, 0, 'conflict count');
});
subtests("Unstowing when target contains real files shouldn't be an issue", sub {
my ($stow) = @_;
plan tests => 4;
# Test both a file which do / don't overlap with the package
make_path('man12/man1');
make_file('man12/man1/alien');
make_file('man12/man1/file12.1');
$stow->plan_unstow('pkg12');
is($stow->get_tasks, 0, 'no tasks to process when unstowing pkg12 for third time');
is($stow->get_conflict_count, 0, 'conflict count');
ok(-f 'man12/man1/alien', 'alien untouched');
ok(-f 'man12/man1/file12.1', 'file overlapping with pkg untouched');
});
subtests("unstow a simple tree minimally when cwd isn't target",
sub {
my $test_dir = shift;
cd($repo);
return {
dir => "$test_dir/stow",
target => "$test_dir/target"
}
},
sub {
my ($stow, $test_dir) = @_;
plan tests => 3;
make_path("$test_dir/stow/pkg13/bin13");
make_file("$test_dir/stow/pkg13/bin13/file13");
make_link("$test_dir/target/bin13", '../stow/pkg13/bin13');
$stow->plan_unstow('pkg13');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(-f "$test_dir/stow/pkg13/bin13/file13", 'package file untouched');
ok(! -e "$test_dir/target/bin13" => 'bin13/ unstowed');
});
subtests("unstow a simple tree minimally with absolute stow dir when cwd isn't target",
sub {
my $test_dir = shift;
cd($repo);
return {
dir => canon_path("$test_dir/stow"),
target => "$test_dir/target"
};
},
sub {
plan tests => 3;
my ($stow, $test_dir) = @_;
make_path("$test_dir/stow/pkg14/bin14");
make_file("$test_dir/stow/pkg14/bin14/file14");
make_link("$test_dir/target/bin14", '../stow/pkg14/bin14');
$stow->plan_unstow('pkg14');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(-f "$test_dir/stow/pkg14/bin14/file14");
ok(! -e "$test_dir/target/bin14"
=> 'unstow a simple tree with absolute stow dir'
);
});
subtests("unstow a simple tree minimally with absolute stow AND target dirs when cwd isn't target",
sub {
my $test_dir = shift;
cd($repo);
return {
dir => canon_path("$test_dir/stow"),
target => canon_path("$test_dir/target")
};
},
sub {
my ($stow, $test_dir) = @_;
plan tests => 3;
make_path("$test_dir/stow/pkg15/bin15");
make_file("$test_dir/stow/pkg15/bin15/file15");
make_link("$test_dir/target/bin15", '../stow/pkg15/bin15');
$stow->plan_unstow('pkg15');
$stow->process_tasks();
is($stow->get_conflict_count, 0, 'conflict count');
ok(-f "$test_dir/stow/pkg15/bin15/file15");
ok(! -e "$test_dir/target/bin15"
=> 'unstow a simple tree with absolute stow and target dirs'
);
});
sub create_and_stow_pkg {
my ($id, $pkg) = @_;
my $stow_pkg = "../stow/$id-$pkg";
make_path($stow_pkg);
make_file("$stow_pkg/$id-file-$pkg");
# create a shallow hierarchy specific to this package and stow
# via folding
make_path("$stow_pkg/$id-$pkg-only-folded");
make_file("$stow_pkg/$id-$pkg-only-folded/file-$pkg");
make_link("$id-$pkg-only-folded", "$stow_pkg/$id-$pkg-only-folded");
# create a deeper hierarchy specific to this package and stow
# via folding
make_path("$stow_pkg/$id-$pkg-only-folded2/subdir");
make_file("$stow_pkg/$id-$pkg-only-folded2/subdir/file-$pkg");
make_link("$id-$pkg-only-folded2",
"$stow_pkg/$id-$pkg-only-folded2");
# create a shallow hierarchy specific to this package and stow
# without folding
make_path("$stow_pkg/$id-$pkg-only-unfolded");
make_file("$stow_pkg/$id-$pkg-only-unfolded/file-$pkg");
make_path("$id-$pkg-only-unfolded");
make_link("$id-$pkg-only-unfolded/file-$pkg",
"../$stow_pkg/$id-$pkg-only-unfolded/file-$pkg");
# create a deeper hierarchy specific to this package and stow
# without folding
make_path("$stow_pkg/$id-$pkg-only-unfolded2/subdir");
make_file("$stow_pkg/$id-$pkg-only-unfolded2/subdir/file-$pkg");
make_path("$id-$pkg-only-unfolded2/subdir");
make_link("$id-$pkg-only-unfolded2/subdir/file-$pkg",
"../../$stow_pkg/$id-$pkg-only-unfolded2/subdir/file-$pkg");
# create a shallow shared hierarchy which this package uses, and stow
# its contents without folding
make_path("$stow_pkg/$id-shared");
make_file("$stow_pkg/$id-shared/file-$pkg");
make_path("$id-shared");
make_link("$id-shared/file-$pkg",
"../$stow_pkg/$id-shared/file-$pkg");
# create a deeper shared hierarchy which this package uses, and stow
# its contents without folding
make_path("$stow_pkg/$id-shared2/subdir");
make_file("$stow_pkg/$id-shared2/file-$pkg");
make_file("$stow_pkg/$id-shared2/subdir/file-$pkg");
make_path("$id-shared2/subdir");
make_link("$id-shared2/file-$pkg",
"../$stow_pkg/$id-shared2/file-$pkg");
make_link("$id-shared2/subdir/file-$pkg",
"../../$stow_pkg/$id-shared2/subdir/file-$pkg");
}
subtest("unstow a tree with no-folding enabled - no refolding should take place", sub {
cd("$TEST_DIR/target");
plan tests => 15;
foreach my $pkg (qw{a b}) {
create_and_stow_pkg('no-folding', $pkg);
}
my $stow = new_Stow('no-folding' => 1);
$stow->plan_unstow('no-folding-b');
is_deeply([ $stow->get_conflicts ], [] => 'no conflicts with --no-folding');
$stow->process_tasks();
is_nonexistent_path('no-folding-b-only-folded');
is_nonexistent_path('no-folding-b-only-folded2');
is_nonexistent_path('no-folding-b-only-unfolded/file-b');
is_nonexistent_path('no-folding-b-only-unfolded2/subdir/file-b');
is_dir_not_symlink('no-folding-shared');
is_dir_not_symlink('no-folding-shared2');
is_dir_not_symlink('no-folding-shared2/subdir');
});
# subtests("Test cleaning up subdirs with --paranoid option", sub {
# TODO
# });

37
test-docker.sh Executable file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Test Stow across multiple Perl versions, by executing the
# Docker image built via build-docker.sh.
#
# Usage: ./test-docker.sh [list | PERL_VERSION]
#
# If the first argument is 'list', list available Perl versions.
# If the first argument is a Perl version, test just that version interactively.
# If no arguments are given test all available Perl versions non-interactively.
version=$( tools/get-version )
if [ -z "$1" ]; then
# Normal non-interactive run
docker run --rm -it \
-v $(pwd):$(pwd) \
-w $(pwd) \
stowtest:$version
elif [ "$1" == list ]; then
# List available Perl versions
docker run --rm -it \
-v $(pwd):$(pwd) \
-v $(pwd)/docker/run-stow-tests.sh:/run-stow-tests.sh \
-w $(pwd) \
-e LIST_PERL_VERSIONS=1 \
stowtest:$version
else
# Interactive run for testing / debugging a particular version
perl_version="$1"
docker run --rm -it \
-v $(pwd):$(pwd) \
-v $(pwd)/docker/run-stow-tests.sh:/run-stow-tests.sh \
-w $(pwd) \
-e PERL_VERSION=$perl_version \
stowtest:$version
fi

File diff suppressed because it is too large Load diff

15
tools/get-version Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/perl
use strict;
use warnings;
open(CONF, "configure.ac") or die "Couldn't open configure.ac: $!\n";
while (<CONF>) {
if (/^AC_INIT\(\[stow\], \[(.+?)\]/) {
print "$1\n";
exit 0;
}
}
exit 1;

View file

@ -1,3 +0,0 @@
@set UPDATED 11 October 1996
@set EDITION 1.3.2
@set VERSION 1.3.2