summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes337
-rw-r--r--LICENSE398
-rw-r--r--MANIFEST34
-rw-r--r--META.yml36
-rw-r--r--MYMETA.json59
-rw-r--r--Makefile.PL40
-rw-r--r--README358
-rw-r--r--inc/Module/Install.pm470
-rw-r--r--inc/Module/Install/Base.pm83
-rw-r--r--inc/Module/Install/Can.pm154
-rw-r--r--inc/Module/Install/Fetch.pm93
-rw-r--r--inc/Module/Install/Makefile.pm418
-rw-r--r--inc/Module/Install/Metadata.pm722
-rw-r--r--inc/Module/Install/Win32.pm64
-rw-r--r--inc/Module/Install/WriteAll.pm63
-rw-r--r--lib/File/HomeDir.pm720
-rw-r--r--lib/File/HomeDir/Darwin.pm152
-rw-r--r--lib/File/HomeDir/Darwin/Carbon.pm210
-rw-r--r--lib/File/HomeDir/Darwin/Cocoa.pm165
-rw-r--r--lib/File/HomeDir/Driver.pm54
-rw-r--r--lib/File/HomeDir/FreeDesktop.pm136
-rw-r--r--lib/File/HomeDir/MacOS9.pm150
-rw-r--r--lib/File/HomeDir/Test.pm137
-rw-r--r--lib/File/HomeDir/Unix.pm186
-rw-r--r--lib/File/HomeDir/Windows.pm241
-rw-r--r--t/01_compile.t37
-rw-r--r--t/02_main.t288
-rw-r--r--t/10_test.t34
-rw-r--r--t/11_darwin.t86
-rw-r--r--t/12_darwin_carbon.t62
-rw-r--r--t/13_darwin_cocoa.t64
-rw-r--r--xt/meta.t27
-rw-r--r--xt/pmv.t32
-rw-r--r--xt/pod.t32
34 files changed, 6142 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..faeb44d
--- /dev/null
+++ b/Changes
@@ -0,0 +1,337 @@
+Changes for Perl extension File-HomeDir
+
+1.00 Fri 19 Oct 2012
+ - No functional changes
+ - Updating to Module::Install 1.06
+ - Don't require documents directory on Mac under AUTOMATED_TESTING
+
+0.99 Thu 26 Jan 2012
+ - Updating to Module::Install 1.04
+ - Removed deprecated interfaces from the documentation
+ - Don't require music and video directories in FreeDesktop tests
+ - The use of deprecated %~ now emits a warning
+
+0.98 Thu 7 Jul 2011
+ - Updating to Module::Install 1.01
+ - If Win32::GetFolderPath returns a \\UNC type path do not do the
+ normal -d sanity check, as strange and unusual bugs may occur.
+
+0.97 Sun 20 Feb 2011
+ - Looks good, moving to production release
+ - This should finally pass on ActivePerl Mac
+
+0.96_04 Tue 1 Feb 2011
+ - Typo in 11_darwin.t
+
+0.96_03 Mon 31 Jan 2011
+ - Return undef in list context on Mac as per the documentation
+
+0.96_02 Mon 31 Jan 2011
+ - No longer assume we have Application Support, sigh
+
+0.96_01 Mon 31 Jan 2011
+ - Removed a dubious "different users have different data" test on Macs
+ - Removed tests for legacy %~ interface
+
+0.95 Mon 31 Jan 2011
+ - Switch to prod version
+
+0.94_01 Tue 14 Dec 2010
+ - More special casing in tests to deal with stripped down non-root
+ Mac environments (mostly to make BINGOS' automated testing pass)
+
+0.93 Wed 13 Sep 2010
+ - Production release, no changes from 0.92_05
+
+0.92_05 Mon 13 Sep 2010
+ - use Mac::SystemDirectory for each Darwin based MacOS. (REHSACK)
+
+0.92_04 Fri 10 Sep 2010
+ - Be less strict about desktop and others on FreeDesktop (ADAMK)
+
+0.92_03 Mon 6 Sep 2010
+ - Adding experimental support for my_dist_config() (JQUELIN)
+ - Adding diag comment on which drivers gets used (ADAMK)
+
+0.92_02 Mon 28 Jun 2010
+ - Updating to Module::Install 1.00 (ADAMK)
+ - Add a bit more docs, and tweak the existing stuff a bit (ADAMK)
+ - Deprecated the %~ interface. It will continue to exist as an
+ undocumented legacy interface until 2015, warnings will be
+ issued from 2013 (ADAMK)
+ - On FreeDesktop.org systems, root often does not have the relevant
+ directories. Skip tests for them in the same way as we do for the
+ Mac root users on darwin (GARU)
+
+0.92_01 Fri 11 Jun 2010
+ - Updating to Module::Install 0.99 (ADAMK)
+ - Adding experimental support for my_dist_data() (JQUELIN)
+
+0.91 Sun 23 May 2010
+ - Moving the FreeDesktop driver to prod
+ - Adding File::HomeDir::Test driver
+
+0.90_04 Fri 12 Feb 2010
+ - Adding missing prereq
+
+0.90_03 Tue 9 Feb 2010
+ - Using FreeDesktop implementation only if xdg-user-script is
+ present, since it's now what's been used internally. This should
+ prevent test failures seen in _02. (JQUELIN)
+
+0.90_02 Thu 14 Jan 2010
+ - Adding support for the alternate FreeBSD xdg directory (JQUELIN)
+ - Improved specification compliance (DAXIM)
+
+0.90_01 Thu 7 Jan 2010
+ - WARNING: This release introduces a major backwards incompatibility
+ for Unix users. The results returned by most methods may change.
+ - Added complete implementation of the FreeDesktop specification and
+ auto-detection of the Unix hosts to which it applies (JQUELIN)
+ - 01_compile.t now loads all backends (since on most platforms,
+ most backends will never normally be loaded).
+
+0.89 Sun 3 Jan 2010
+ - Loosen the testing intensity on Darwin Carbon backends to prevent
+ issues with consumer directories prevent installation entirely.
+
+0.88 Tue 24 Nov 2009
+ - Switching to a production release
+
+0.87_01 Sat 3 Oct 2009
+ - First developer implementation of improved Mac support
+
+0.86 Fri 27 Mar 2009
+ - Bug fix for the 64-bit implementation
+
+0.85_01 Fri 27 Mar 2009
+ - For 64-bit perl on Darwin, fall back to File::HomeDir::Unix
+ as Mac::Carbon is not available
+
+0.84 Wed 11 Mar 2009
+ - Adds support for $ENV{HOME} on Darwin
+ - Other bug fixes on Darwin (MIYAGAWA)
+
+0.83_01 Sat 1 Nov 2008
+ - Patch from Darin McBride to fix user_home on Darwin.
+
+0.82 Tue 14 Oct 2008
+ - When we get more than one warning, diag the warnings
+ so that we have actually have a chance to get rid of
+ the extra warnings.
+
+0.81 Thu 3 Jul 2008
+ - Updating to Module::Install 0.77
+ - Localising $@ during evals
+ - Updating Perl version dependency to 5.00503
+
+0.80 Fri 27 Jun 2008
+ - All clear on the CPAN Testers front, flipping to production
+
+0.71_03 Wed 25 Jun 2008
+ - Removing the Server 2003 and 2008 skips that should work
+ now that we create directories on demand.
+ - File::HomeDir should now support "Perl on a Stick"
+
+0.71_02 Mon 28 Apr 2008
+ - Added `my_dot_config`.
+ - Adding a base driver class.
+
+0.71_01 Fri 4 Apr 2008
+ - Converted from Registry checks to Win32 calls.
+ This includes giving it the "create directory" call.
+ - Removing the dependency on the registry modules.
+
+0.70 Fri 28 Feb 2008
+ - Windows Server 2003 does not have Music/Pictures/etc directories
+ (correct the test to not expect them to exist)
+ - Make the same assumption about Windows Server 2008
+
+0.69 Sun 3 Feb 2008
+ - No changes, incrementing for production release
+
+0.68_01 Tue 22 Jan 2008
+ - Fixed folder detection on Darwin so that symlinks that resolve to
+ directories are considered valid folders. Patch from David Wheeler.
+
+0.67 Thu 6 Dec 2007
+ - No functional changes, no need to upgrade.
+ - Upgrading to Module::Install 0.68
+ - Updating bundled author tests
+
+0.66 Sat 25 Aug 2007
+ - No functional changes, no need to upgrade.
+ (This release attempts to regain 100% CPAN Testers results)
+ - Spurious failures on some path-levels of 5.9.0 due to a warnings
+ bug regression. Skip the relevant test on Perl 5.9.0.
+ - Remove a -w flag in 02_main.t so test run under tainting
+
+0.65 Wed 21 Mar 2007
+ - Add a special case to pass users_home(current user) on to my_home
+ (This prevents tests failing when you manually set HOME to lie
+ about your home directory. This was mostly preventing installation
+ with "sudo cpan -i File::HomeDir".
+ - Upgraded to Module::Install 0.65
+
+0.64 Thu 8 Feb 2007 (Stephen Steneker)
+ - Add Makefile prequisite for a version of Mac::Carbon that properly supports Intel macs .. default Tiger install includes a buggy version [RT#24222]
+ - No other changes from 0.63
+
+0.63 Tue 9 Jan 2007
+ - The ability to overload HOME on any platform, even Win32, is
+ apparently desirable. So now we support the use of HOME on Win32
+ for that specific case.
+
+0.62 Thu 2 Jan 2007
+ - On WinXP, the My Videos directory (and registry entry) does not
+ exist by default. It is created the first time Windows Movie Maker
+ is run.
+ - Skip the My Videos test on WinXP as a result
+
+0.61 Tue 2 Jan 2007
+ - Verified the previous version on Win2K, WinXP, Linux and Mac OS X.
+ - Verified as a normal user, root and nobody on most of these.
+ - No change other than converting the version to a release version.
+
+0.60_13 Tue 1 Jan 2007
+ - Lets try that again
+
+0.60_12 Mon 1 Jan 2007
+ - Skip an unreliable test on older Perls
+
+0.60_11 Mon 1 Jan 2007
+ - Reduced the basic version dependencies on Mac OS because they
+ were unnecesary on that platform, and now Mac users don't need
+ to upgrade PathTools.
+
+0.60_10 Mon 1 Jan 2007
+ - More testing problems on Win2K
+ - Adding a dependency on Win32 to access Win32::GetOSName
+ (Dependency issues could make this backfire, so maybe will
+ need another test cycle or two to make sure it works)
+
+0.60_09 Mon 1 Jan 2007
+ - Sigh... cases are now known to exist where users
+ do not have home directories. Tests refactored
+ AGAIN do allow the "nobody" user to pass the test
+ suite. Don't ask me WHY people might need to install
+ a module as nobody. I don't know.
+
+ But now the test suite accomodates that.
+
+ - On Unix, if the home directory does not exist, for
+ example /nonexistant, it means the user does NOT have
+ a home directory. So in those cases, return undef
+ instead of the /nonexistant directory.
+
+0.60_08 Mon 1 Jan 2007
+ - Problems with Win2k hopefully finally resolved
+
+0.60_07 Tue 19 Dec 2006
+ - Problems with testing continue to plague the module...
+
+0.60_06 Fri 15 Dec 2006
+ - Another attempt to fix the getpwuid problem
+
+0.60_05 Tue 12 Dec 2006
+ - Removed a build-time dependency on getpwuid
+
+0.60_04 Thu 2 Nov 2006 Stephen Steneker stennie@cpan.org
+ - win32: add support for my_pictures, my_videos
+ - darwin: add support for my_music, my_pictures, my_videos
+ - Skip "root" tests on darwin, not supported
+ - add POD docs with examples for o/s specific implementations
+
+0.60_03 Wed 20 Sep 2006
+ - Cleaned up the way unimplemented method exceptions are thrown.
+ - Fleshed out the docs a bit more.
+ - Added an initial implementation of my_music
+
+0.60_02 Fri 14 Jul 2006
+ - Altered testing to allow cases where there are no "toys" directories
+ - More cleanups for Darwin in the root case
+ - Updating dependencies to something more modern
+ (mostly to ensure certain fixes to certain problems exist)
+
+0.60_01 ...
+ # Introduces back-compatibility issues
+ - No longer treat lack of a home directory as an error
+ - More test written on the Israel.pm monthly meeting
+ - Major upgrade to Darwin driver (CNANDOR)
+
+0.58 Wed 10 May 2006
+ # No functional changes, upgrading has no benefit.
+ - Upgrade Module::Install to 0.62 final
+ - AutoInstall is only needed for options, so remove auto_install
+
+0.57 2006-03-10 Adam Kennedy adamk@cpan.org
+ # No functional changes, upgrading has no benefit.
+ - Upgraded Module::Install to 0.62
+ (M:I is relatively sane from 0.61)
+ - Removing all use of UNIVERSAL::isa (the function)
+ - Adding missing use Carp() in a couple of cases
+ - Minor POD changes
+
+0.56 2006-03-10 Adam Kennedy adamk@cpan.org
+ # No functional changes, upgrading has no benefit.
+ - I screwed up Module::Install 0.58
+ - Fixed that, then incremented version to fix this
+
+0.55 2006-03-05 Adam Kennedy adamk@cpan.org
+ # No functional changes, upgrading has no benefit.
+ - Documentation bug fix
+ - Documented the todo list
+ - Updated Module::Install to 0.58
+
+0.54 2006-02-27 Adam Kennedy adamk@cpan.org
+ - Adding a dependency of Win32::TieRegistry's, so this installs.
+ - Will remove it later when that bug is fixed in Win32::TieRegistry
+
+0.53 2006-02-27 Adam Kennedy adamk@cpan.org
+ - Typo caused Makefile.PL not to require Win32::TieRegistry on Win32
+ - Upgraded to Module::Install 0.57
+
+0.52 2005-01-04 Adam Kennedy adamk@cpan.org
+ - Added initial Darwin support.
+
+0.51 2005-12-30 Adam Kennedy adamk@cpan.org
+ - Fixed a typo where I left the require of the Windows module
+ as Win32. (Randy Kobes)
+
+0.50 2005-12-26 Adam Kennedy adamk@cpan.org
+ - Rewrote the guts entirely to split functionality out into
+ platform-specific submodules, and to add more specialised
+ code for Win32.
+
+0.07 2005-11-09 Adam Kennedy adamk@cpan.org
+ - Near-complete rewrite to modernise and prepare to
+ start merging in File::HomeDir::Win32.
+
+ - "Traded" module in exchange for Data::JavaScript::Anon :)
+ - Replaced Makefile.PL with Module::Install-based version that lists
+ its dependencies in a platform-sensitive way.
+ This also removes the need for evals.
+ - Replaced tests with Test::More-based ones and improved
+ coverage.
+ - Put the platform-specific code into if ( CONSTANT ) blocks
+ so they will compile out.
+ - Otherwise cleaned up and improved the layout of the code
+ - Added support for $ENV{HOMEDIR} and $ENV{HOMEPATH} on Win32
+ - More-explicit testing before we return a path
+ - Keep caching user home, but NOT "my" home in case the
+ process changes user.
+
+0.06 2004-12-29 Sean M. Burke sburke@cpan.org
+ # No functional changes, upgrading has no benefit.
+ - just rebundling. No code changes.
+
+0.05 2000-12-09 Sean M. Burke sburke@cpan.org
+ - adding MSWin code to consult the registry,
+ as helpfully suggested by Richard Soderberg <rs@crystalflame.net>.
+ - Tweaked MacPerl code a bit.
+
+0.04 2000-12-09 Sean M. Burke sburke@cpan.org
+ - just fixing incidental typos in the POD.
+
+0.03 2000-12-08 Sean M. Burke sburke@cpan.org
+ - first public release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e455655
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,398 @@
+
+Terms of Perl itself
+
+a) the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any
+ later version, or
+b) the "Artistic License"
+
+----------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
+----------------------------------------------------------------------------
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of the
+package the right to use and distribute the Package in a more-or-less customary
+fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+- "Package" refers to the collection of files distributed by the Copyright
+ Holder, and derivatives of that collection of files created through textual
+ modification.
+- "Standard Version" refers to such a Package if it has not been modified,
+ or has been modified in accordance with the wishes of the Copyright
+ Holder.
+- "Copyright Holder" is whoever is named in the copyright or copyrights for
+ the package.
+- "You" is you, if you're thinking about copying or distributing this Package.
+- "Reasonable copying fee" is whatever you can justify on the basis of
+ media cost, duplication charges, time of people involved, and so on. (You
+ will not be required to justify it to the Copyright Holder, but only to the
+ computing community at large as a market that must bear the fee.)
+- "Freely Available" means that no fee is charged for the item itself, though
+ there may be fees involved in handling the item. It also means that
+ recipients of the item may redistribute it under the same conditions they
+ received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you duplicate
+all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived from
+the Public Domain or from the Copyright Holder. A Package modified in such a
+way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and when
+you changed that file, and provided that you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said modifications
+ to Usenet or an equivalent medium, or placing the modifications on
+ a major archive site such as ftp.uu.net, or by allowing the
+ Copyright Holder to include your modifications in the Standard
+ Version of the Package.
+
+ b) use the modified Package only within your corporation or
+ organization.
+
+ c) rename any non-standard executables so the names do not
+ conflict with standard executables, which must also be provided,
+ and provide a separate manual page for each non-standard
+ executable that clearly documents how it differs from the Standard
+ Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library
+ files, together with instructions (in the manual page or equivalent)
+ on where to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) accompany any non-standard executables with their
+ corresponding Standard Version executables, giving the
+ non-standard executables non-standard names, and clearly
+ documenting the differences in manual pages (or equivalent),
+ together with instructions on where to get the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this Package.
+You may charge any fee you choose for support of this Package. You may not
+charge a fee for this Package itself. However, you may distribute this Package in
+aggregate with other (possibly commercial) programs as part of a larger
+(possibly commercial) software distribution provided that you do not advertise
+this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output from
+the programs of this Package do not automatically fall under the copyright of this
+Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The End
+
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..20c0ab7
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,34 @@
+Changes
+inc/Module/Install.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
+lib/File/HomeDir.pm
+lib/File/HomeDir/Darwin.pm
+lib/File/HomeDir/Darwin/Carbon.pm
+lib/File/HomeDir/Darwin/Cocoa.pm
+lib/File/HomeDir/Driver.pm
+lib/File/HomeDir/FreeDesktop.pm
+lib/File/HomeDir/MacOS9.pm
+lib/File/HomeDir/Test.pm
+lib/File/HomeDir/Unix.pm
+lib/File/HomeDir/Windows.pm
+LICENSE
+Makefile.PL
+MANIFEST This list of files
+META.yml
+MYMETA.json
+README
+t/01_compile.t
+t/02_main.t
+t/10_test.t
+t/11_darwin.t
+t/12_darwin_carbon.t
+t/13_darwin_cocoa.t
+xt/meta.t
+xt/pmv.t
+xt/pod.t
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..8688553
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,36 @@
+---
+abstract: 'Find your home and other directories on any platform'
+author:
+ - 'Adam Kennedy <adamk@cpan.org>'
+build_requires:
+ ExtUtils::MakeMaker: 6.36
+ Test::More: 0.47
+configure_requires:
+ ExtUtils::MakeMaker: 6.36
+distribution_type: module
+dynamic_config: 1
+generated_by: 'Module::Install version 1.06'
+license: perl
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: 1.4
+module_name: File::HomeDir
+name: File-HomeDir
+no_index:
+ directory:
+ - inc
+ - t
+ - xt
+requires:
+ Carp: 0
+ Cwd: 3.12
+ File::Path: 2.01
+ File::Spec: 3.12
+ File::Temp: 0.19
+ File::Which: 0.05
+ perl: 5.00503
+resources:
+ ChangeLog: http://fisheye2.atlassian.com/changelog/cpan/trunk/File-HomeDir
+ license: http://dev.perl.org/licenses/
+ repository: http://svn.ali.as/cpan/trunk/File-HomeDir
+version: 1.00
diff --git a/MYMETA.json b/MYMETA.json
new file mode 100644
index 0000000..6f47a6d
--- /dev/null
+++ b/MYMETA.json
@@ -0,0 +1,59 @@
+{
+ "abstract" : "Find your home and other directories on any platform",
+ "author" : [
+ "Adam Kennedy <adamk@cpan.org>"
+ ],
+ "dynamic_config" : 0,
+ "generated_by" : "Module::Install version 1.06, CPAN::Meta::Converter version 2.120921",
+ "license" : [
+ "perl_5"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : "2"
+ },
+ "name" : "File-HomeDir",
+ "no_index" : {
+ "directory" : [
+ "inc",
+ "t",
+ "xt"
+ ]
+ },
+ "prereqs" : {
+ "build" : {
+ "requires" : {
+ "ExtUtils::MakeMaker" : "6.36",
+ "Test::More" : "0.47"
+ }
+ },
+ "configure" : {
+ "requires" : {
+ "ExtUtils::MakeMaker" : "6.36"
+ }
+ },
+ "runtime" : {
+ "requires" : {
+ "Carp" : "0",
+ "Cwd" : "3.12",
+ "File::Path" : "2.01",
+ "File::Spec" : "3.12",
+ "File::Temp" : "0.19",
+ "File::Which" : "0.05",
+ "perl" : "5.00503"
+ }
+ }
+ },
+ "release_status" : "stable",
+ "resources" : {
+ "license" : [
+ "http://dev.perl.org/licenses/"
+ ],
+ "repository" : {
+ "url" : "http://svn.ali.as/cpan/trunk/File-HomeDir"
+ },
+ "x_ChangeLog" : "http://fisheye2.atlassian.com/changelog/cpan/trunk/File-HomeDir"
+ },
+ "version" : "1.00",
+ "x_module_name" : "File::HomeDir"
+}
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..25d6b11
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,40 @@
+use inc::Module::Install 1.06;
+use Config;
+
+all_from 'lib/File/HomeDir.pm';
+requires 'Carp' => 0;
+requires 'Cwd' => $^O eq 'darwin' ? '3' : '3.12';
+requires 'File::Spec' => $^O eq 'darwin' ? '3' : '3.12';
+requires 'File::Path' => '2.01';
+requires 'File::Temp' => '0.19';
+requires 'File::Which' => '0.05';
+test_requires 'Test::More' => '0.47';
+
+# Dependencies for specific platforms
+### Use variable twice to avoid a warning
+if ( $MacPerl::Version and $MacPerl::Version
+ or $^O eq 'darwin' and _check_old_mac_os_x() ) {
+ # Old Mac OS 9, or Mac OS X before Mac::SystemDirectory is supported.
+ requires 'Mac::Files' => 0;
+
+} elsif ( $^O eq 'darwin' ) {
+ # Modern Max OS X and Darwin OSS releases for 32bit and 64bit
+ requires 'Mac::SystemDirectory' => '0.04';
+
+} elsif ( $^O eq 'MSWin32' ) {
+ requires 'Win32' => '0.31';
+}
+
+WriteAll;
+
+sub _check_old_mac_os_x {
+ local $@;
+ $Config{ptrsize} == 8 and return;
+ return eval {
+ require POSIX;
+ my $release = ( POSIX::uname() )[2];
+ my ( $major ) = split qr{ [.] }smx, $release;
+ # 'old' means before darwin 8 = Mac OS 10.4 = Tiger
+ $major < 8;
+ };
+}
diff --git a/README b/README
new file mode 100644
index 0000000..a3e05f4
--- /dev/null
+++ b/README
@@ -0,0 +1,358 @@
+NAME
+ File::HomeDir - Find your home and other directories on any platform
+
+SYNOPSIS
+ use File::HomeDir;
+
+ # Modern Interface (Current User)
+ $home = File::HomeDir->my_home;
+ $desktop = File::HomeDir->my_desktop;
+ $docs = File::HomeDir->my_documents;
+ $music = File::HomeDir->my_music;
+ $pics = File::HomeDir->my_pictures;
+ $videos = File::HomeDir->my_videos;
+ $data = File::HomeDir->my_data;
+ $dist = File::HomeDir->my_dist_data('File-HomeDir');
+ $dist = File::HomeDir->my_dist_config('File-HomeDir');
+
+ # Modern Interface (Other Users)
+ $home = File::HomeDir->users_home('foo');
+ $desktop = File::HomeDir->users_desktop('foo');
+ $docs = File::HomeDir->users_documents('foo');
+ $music = File::HomeDir->users_music('foo');
+ $pics = File::HomeDir->users_pictures('foo');
+ $video = File::HomeDir->users_videos('foo');
+ $data = File::HomeDir->users_data('foo');
+
+DESCRIPTION
+ File::HomeDir is a module for locating the directories that are "owned"
+ by a user (typicaly your user) and to solve the various issues that
+ arise trying to find them consistently across a wide variety of
+ platforms.
+
+ The end result is a single API that can find your resources on any
+ platform, making it relatively trivial to create Perl software that
+ works elegantly and correctly no matter where you run it.
+
+ This module provides two main interfaces.
+
+ The first is a modern File::Spec-style interface with a consistent OO
+ API and different implementation modules to support various platforms.
+ You are strongly recommended to use this interface.
+
+ The second interface is for legacy support of the original 0.07
+ interface that exported a "home()" function by default and tied the "%~"
+ variable.
+
+ It is generally not recommended that you use this interface, but due to
+ back-compatibility reasons they will remain supported until at least
+ 2010.
+
+ The "%~" interface has been deprecated. Documentation was removed in
+ 2009, Unit test were removed in 2011, usage will issue warnings from
+ 2012, and the interface will be removed entirely in 2015 (in line with
+ the general Perl toolchain convention of a 10 year support period for
+ legacy APIs that are potentially or actually in common use).
+
+ Platform Neutrality
+ In the Unix world, many different types of data can be mixed together in
+ your home directory (although on some Unix platforms this is no longer
+ the case, particularly for "desktop"-oriented platforms).
+
+ On some non-Unix platforms, separate directories are allocated for
+ different types of data and have been for a long time.
+
+ When writing applications on top of File::HomeDir, you should thus
+ always try to use the most specific method you can. User documents
+ should be saved in "my_documents", data that supports an application but
+ isn't normally editing by the user directory should go into "my_data".
+
+ On platforms that do not make any distinction, all these different
+ methods will harmlessly degrade to the main home directory, but on
+ platforms that care File::HomeDir will always try to Do The Right
+ Thing(tm).
+
+METHODS
+ Two types of methods are provided. The "my_method" series of methods for
+ finding resources for the current user, and the "users_method" (read as
+ "user's method") series for finding resources for arbitrary users.
+
+ This split is necessary, as on most platforms it is much easier to find
+ information about the current user compared to other users, and indeed
+ on a number you cannot find out information such as "users_desktop" at
+ all, due to security restrictions.
+
+ All methods will double check (using a "-d" test) that a directory
+ actually exists before returning it, so you may trust in the values that
+ are returned (subject to the usual caveats of race conditions of
+ directories being deleted at the moment between a directory being
+ returned and you using it).
+
+ However, because in some cases platforms may not support the concept of
+ home directories at all, any method may return "undef" (both in scalar
+ and list context) to indicate that there is no matching directory on the
+ system.
+
+ For example, most untrusted 'nobody'-type users do not have a home
+ directory. So any modules that are used in a CGI application that at
+ some level of recursion use your code, will result in calls to
+ File::HomeDir returning undef, even for a basic home() call.
+
+ my_home
+ The "my_home" method takes no arguments and returns the main
+ home/profile directory for the current user.
+
+ If the distinction is important to you, the term "current" refers to the
+ real user, and not the effective user.
+
+ This is also the case for all of the other "my" methods.
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a home directory, or dies on error.
+
+ my_desktop
+ The "my_desktop" method takes no arguments and returns the "desktop"
+ directory for the current user.
+
+ Due to the diversity and complexity of implementions required to deal
+ with implementing the required functionality fully and completely, the
+ "my_desktop" method may or may not be implemented on each platform.
+
+ That said, I am extremely interested in code to implement "my_desktop"
+ on Unix, as long as it is capable of dealing (as the Windows
+ implementation does) with internationalisation. It should also avoid
+ false positive results by making sure it only returns the appropriate
+ directories for the appropriate platforms.
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a desktop directory, or dies on error.
+
+ my_documents
+ The "my_documents" method takes no arguments and returns the directory
+ (for the current user) where the user's documents are stored.
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a documents directory, or dies on error.
+
+ my_music
+ The "my_music" method takes no arguments and returns the directory where
+ the current user's music is stored.
+
+ No bias is made to any particular music type or music program, rather
+ the concept of a directory to hold the user's music is made at the level
+ of the underlying operating system or (at least) desktop environment.
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a suitable directory, or dies on error.
+
+ my_pictures
+ The "my_pictures" method takes no arguments and returns the directory
+ where the current user's pictures are stored.
+
+ No bias is made to any particular picture type or picture program,
+ rather the concept of a directory to hold the user's pictures is made at
+ the level of the underlying operating system or (at least) desktop
+ environment.
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a suitable directory, or dies on error.
+
+ my_videos
+ The "my_videos" method takes no arguments and returns the directory
+ where the current user's videos are stored.
+
+ No bias is made to any particular video type or video program, rather
+ the concept of a directory to hold the user's videos is made at the
+ level of the underlying operating system or (at least) desktop
+ environment.
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a suitable directory, or dies on error.
+
+ my_data
+ The "my_data" method takes no arguments and returns the directory where
+ local applications should stored their internal data for the current
+ user.
+
+ Generally an application would create a subdirectory such as ".foo",
+ beneath this directory, and store its data there. By creating your
+ directory this way, you get an accurate result on the maximum number of
+ platforms. But see the documentation about "my_dist_config()" or
+ "my_dist_data()" below.
+
+ For example, on Unix you get "~/.foo" and on Win32 you get "~/Local
+ Settings/Application Data/.foo"
+
+ Returns the directory path as a string, "undef" if the current user does
+ not have a data directory, or dies on error.
+
+ my_dist_config
+ File::HomeDir->my_dist_config( $dist [, \%params] );
+
+ # For example...
+
+ File::HomeDir->my_dist_config( 'File-HomeDir' );
+ File::HomeDir->my_dist_config( 'File-HomeDir', { create => 1 } );
+
+ The "my_dist_config" method takes a distribution name as argument and
+ returns an application-specific directory where they should store their
+ internal configuration.
+
+ The base directory will be either "my_config" if the platform supports
+ it, or "my_documents" otherwise. The subdirectory itself will be
+ "BASE/Perl/Dist-Name". If the base directory is the user's homedir,
+ "my_dist_config" will be in "~/.perl/Dist-Name" (and thus be hidden on
+ all Unixes).
+
+ The optional last argument is a hash reference to tweak the method
+ behaviour. The following hash keys are recognized:
+
+ * create
+
+ Passing a true value to this key will force the creation of the
+ directory if it doesn't exist (remember that "File::HomeDir"'s
+ policy is to return "undef" if the directory doesn't exist).
+
+ Defaults to false, meaning no automatic creation of directory.
+
+ my_dist_data
+ File::HomeDir->my_dist_data( $dist [, \%params] );
+
+ # For example...
+
+ File::HomeDir->my_dist_data( 'File-HomeDir' );
+ File::HomeDir->my_dist_data( 'File-HomeDir', { create => 1 } );
+
+ The "my_dist_data" method takes a distribution name as argument and
+ returns an application-specific directory where they should store their
+ internal data.
+
+ This directory will be of course a subdirectory of "my_data". Platforms
+ supporting data-specific directories will use
+ "DATA_DIR/perl/dist/Dist-Name" following the common
+ "DATA/vendor/application" pattern. If the "my_data" directory is the
+ user's homedir, "my_dist_data" will be in "~/.perl/dist/Dist-Name" (and
+ thus be hidden on all Unixes).
+
+ The optional last argument is a hash reference to tweak the method
+ behaviour. The following hash keys are recognized:
+
+ * create
+
+ Passing a true value to this key will force the creation of the
+ directory if it doesn't exist (remember that "File::HomeDir"'s
+ policy is to return "undef" if the directory doesn't exist).
+
+ Defaults to false, meaning no automatic creation of directory.
+
+ users_home
+ $home = File::HomeDir->users_home('foo');
+
+ The "users_home" method takes a single param and is used to locate the
+ parent home/profile directory for an identified user on the system.
+
+ While most of the time this identifier would be some form of user name,
+ it is permitted to vary per-platform to support user ids or UUIDs as
+ applicable for that platform.
+
+ Returns the directory path as a string, "undef" if that user does not
+ have a home directory, or dies on error.
+
+ users_documents
+ $docs = File::HomeDir->users_documents('foo');
+
+ Returns the directory path as a string, "undef" if that user does not
+ have a documents directory, or dies on error.
+
+ users_data
+ $data = File::HomeDir->users_data('foo');
+
+ Returns the directory path as a string, "undef" if that user does not
+ have a data directory, or dies on error.
+
+FUNCTIONS
+ home
+ use File::HomeDir;
+ $home = home();
+ $home = home('foo');
+ $home = File::HomeDir::home();
+ $home = File::HomeDir::home('foo');
+
+ The "home" function is exported by default and is provided for
+ compatibility with legacy applications. In new applications, you should
+ use the newer method-based interface above.
+
+ Returns the directory path to a named user's home/profile directory.
+
+ If provided no param, returns the directory path to the current user's
+ home/profile directory.
+
+TO DO
+ * Add more granularity to Unix, and add support to VMS and other
+ esoteric platforms, so we can consider going core.
+
+ * Add consistent support for users_* methods
+
+SUPPORT
+ This module is stored in an Open Repository at the following address.
+
+ <http://svn.ali.as/cpan/trunk/File-HomeDir>
+
+ Write access to the repository is made available automatically to any
+ published CPAN author, and to most other volunteers on request.
+
+ If you are able to submit your bug report in the form of new (failing)
+ unit tests, or can apply your fix directly instead of submitting a
+ patch, you are strongly encouraged to do so as the author currently
+ maintains over 100 modules and it can take some time to deal with
+ non-Critical bug reports or patches.
+
+ This will guarantee that your issue will be addressed in the next
+ release of the module.
+
+ If you cannot provide a direct test or fix, or don't have time to do so,
+ then regular bug reports are still accepted and appreciated via the CPAN
+ bug tracker.
+
+ <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-HomeDir>
+
+ For other issues, for commercial enhancement or support, or to have your
+ write access enabled for the repository, contact the author at the email
+ address above.
+
+ACKNOWLEDGEMENTS
+ The biggest acknowledgement goes to Chris Nandor, who wielded his
+ legendary Mac-fu and turned my initial fairly ordinary Darwin
+ implementation into something that actually worked properly everywhere,
+ and then donated a Mac OS X license to allow it to be maintained
+ properly.
+
+AUTHORS
+ Adam Kennedy <adamk@cpan.org>
+
+ Sean M. Burke <sburke@cpan.org>
+
+ Chris Nandor <cnandor@cpan.org>
+
+ Stephen Steneker <stennie@cpan.org>
+
+SEE ALSO
+ File::ShareDir, File::HomeDir::Win32 (legacy)
+
+COPYRIGHT
+ Copyright 2005 - 2012 Adam Kennedy.
+
+ Some parts copyright 2000 Sean M. Burke.
+
+ Some parts copyright 2006 Chris Nandor.
+
+ Some parts copyright 2006 Stephen Steneker.
+
+ Some parts copyright 2009-2011 Jérôme Quelin.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the same terms as Perl itself.
+
+ The full text of the license can be found in the LICENSE file included
+ with this module.
+
diff --git a/inc/Module/Install.pm b/inc/Module/Install.pm
new file mode 100644
index 0000000..4ecf46b
--- /dev/null
+++ b/inc/Module/Install.pm
@@ -0,0 +1,470 @@
+#line 1
+package Module::Install;
+
+# For any maintainers:
+# The load order for Module::Install is a bit magic.
+# It goes something like this...
+#
+# IF ( host has Module::Install installed, creating author mode ) {
+# 1. Makefile.PL calls "use inc::Module::Install"
+# 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
+# 3. The installed version of inc::Module::Install loads
+# 4. inc::Module::Install calls "require Module::Install"
+# 5. The ./inc/ version of Module::Install loads
+# } ELSE {
+# 1. Makefile.PL calls "use inc::Module::Install"
+# 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
+# 3. The ./inc/ version of Module::Install loads
+# }
+
+use 5.005;
+use strict 'vars';
+use Cwd ();
+use File::Find ();
+use File::Path ();
+
+use vars qw{$VERSION $MAIN};
+BEGIN {
+ # All Module::Install core packages now require synchronised versions.
+ # This will be used to ensure we don't accidentally load old or
+ # different versions of modules.
+ # This is not enforced yet, but will be some time in the next few
+ # releases once we can make sure it won't clash with custom
+ # Module::Install extensions.
+ $VERSION = '1.06';
+
+ # Storage for the pseudo-singleton
+ $MAIN = undef;
+
+ *inc::Module::Install::VERSION = *VERSION;
+ @inc::Module::Install::ISA = __PACKAGE__;
+
+}
+
+sub import {
+ my $class = shift;
+ my $self = $class->new(@_);
+ my $who = $self->_caller;
+
+ #-------------------------------------------------------------
+ # all of the following checks should be included in import(),
+ # to allow "eval 'require Module::Install; 1' to test
+ # installation of Module::Install. (RT #51267)
+ #-------------------------------------------------------------
+
+ # Whether or not inc::Module::Install is actually loaded, the
+ # $INC{inc/Module/Install.pm} is what will still get set as long as
+ # the caller loaded module this in the documented manner.
+ # If not set, the caller may NOT have loaded the bundled version, and thus
+ # they may not have a MI version that works with the Makefile.PL. This would
+ # result in false errors or unexpected behaviour. And we don't want that.
+ my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
+ unless ( $INC{$file} ) { die <<"END_DIE" }
+
+Please invoke ${\__PACKAGE__} with:
+
+ use inc::${\__PACKAGE__};
+
+not:
+
+ use ${\__PACKAGE__};
+
+END_DIE
+
+ # This reportedly fixes a rare Win32 UTC file time issue, but
+ # as this is a non-cross-platform XS module not in the core,
+ # we shouldn't really depend on it. See RT #24194 for detail.
+ # (Also, this module only supports Perl 5.6 and above).
+ eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006;
+
+ # If the script that is loading Module::Install is from the future,
+ # then make will detect this and cause it to re-run over and over
+ # again. This is bad. Rather than taking action to touch it (which
+ # is unreliable on some platforms and requires write permissions)
+ # for now we should catch this and refuse to run.
+ if ( -f $0 ) {
+ my $s = (stat($0))[9];
+
+ # If the modification time is only slightly in the future,
+ # sleep briefly to remove the problem.
+ my $a = $s - time;
+ if ( $a > 0 and $a < 5 ) { sleep 5 }
+
+ # Too far in the future, throw an error.
+ my $t = time;
+ if ( $s > $t ) { die <<"END_DIE" }
+
+Your installer $0 has a modification time in the future ($s > $t).
+
+This is known to create infinite loops in make.
+
+Please correct this, then run $0 again.
+
+END_DIE
+ }
+
+
+ # Build.PL was formerly supported, but no longer is due to excessive
+ # difficulty in implementing every single feature twice.
+ if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
+
+Module::Install no longer supports Build.PL.
+
+It was impossible to maintain duel backends, and has been deprecated.
+
+Please remove all Build.PL files and only use the Makefile.PL installer.
+
+END_DIE
+
+ #-------------------------------------------------------------
+
+ # To save some more typing in Module::Install installers, every...
+ # use inc::Module::Install
+ # ...also acts as an implicit use strict.
+ $^H |= strict::bits(qw(refs subs vars));
+
+ #-------------------------------------------------------------
+
+ unless ( -f $self->{file} ) {
+ foreach my $key (keys %INC) {
+ delete $INC{$key} if $key =~ /Module\/Install/;
+ }
+
+ local $^W;
+ require "$self->{path}/$self->{dispatch}.pm";
+ File::Path::mkpath("$self->{prefix}/$self->{author}");
+ $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
+ $self->{admin}->init;
+ @_ = ($class, _self => $self);
+ goto &{"$self->{name}::import"};
+ }
+
+ local $^W;
+ *{"${who}::AUTOLOAD"} = $self->autoload;
+ $self->preload;
+
+ # Unregister loader and worker packages so subdirs can use them again
+ delete $INC{'inc/Module/Install.pm'};
+ delete $INC{'Module/Install.pm'};
+
+ # Save to the singleton
+ $MAIN = $self;
+
+ return 1;
+}
+
+sub autoload {
+ my $self = shift;
+ my $who = $self->_caller;
+ my $cwd = Cwd::cwd();
+ my $sym = "${who}::AUTOLOAD";
+ $sym->{$cwd} = sub {
+ my $pwd = Cwd::cwd();
+ if ( my $code = $sym->{$pwd} ) {
+ # Delegate back to parent dirs
+ goto &$code unless $cwd eq $pwd;
+ }
+ unless ($$sym =~ s/([^:]+)$//) {
+ # XXX: it looks like we can't retrieve the missing function
+ # via $$sym (usually $main::AUTOLOAD) in this case.
+ # I'm still wondering if we should slurp Makefile.PL to
+ # get some context or not ...
+ my ($package, $file, $line) = caller;
+ die <<"EOT";
+Unknown function is found at $file line $line.
+Execution of $file aborted due to runtime errors.
+
+If you're a contributor to a project, you may need to install
+some Module::Install extensions from CPAN (or other repository).
+If you're a user of a module, please contact the author.
+EOT
+ }
+ my $method = $1;
+ if ( uc($method) eq $method ) {
+ # Do nothing
+ return;
+ } elsif ( $method =~ /^_/ and $self->can($method) ) {
+ # Dispatch to the root M:I class
+ return $self->$method(@_);
+ }
+
+ # Dispatch to the appropriate plugin
+ unshift @_, ( $self, $1 );
+ goto &{$self->can('call')};
+ };
+}
+
+sub preload {
+ my $self = shift;
+ unless ( $self->{extensions} ) {
+ $self->load_extensions(
+ "$self->{prefix}/$self->{path}", $self
+ );
+ }
+
+ my @exts = @{$self->{extensions}};
+ unless ( @exts ) {
+ @exts = $self->{admin}->load_all_extensions;
+ }
+
+ my %seen;
+ foreach my $obj ( @exts ) {
+ while (my ($method, $glob) = each %{ref($obj) . '::'}) {
+ next unless $obj->can($method);
+ next if $method =~ /^_/;
+ next if $method eq uc($method);
+ $seen{$method}++;
+ }
+ }
+
+ my $who = $self->_caller;
+ foreach my $name ( sort keys %seen ) {
+ local $^W;
+ *{"${who}::$name"} = sub {
+ ${"${who}::AUTOLOAD"} = "${who}::$name";
+ goto &{"${who}::AUTOLOAD"};
+ };
+ }
+}
+
+sub new {
+ my ($class, %args) = @_;
+
+ delete $INC{'FindBin.pm'};
+ {
+ # to suppress the redefine warning
+ local $SIG{__WARN__} = sub {};
+ require FindBin;
+ }
+
+ # ignore the prefix on extension modules built from top level.
+ my $base_path = Cwd::abs_path($FindBin::Bin);
+ unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
+ delete $args{prefix};
+ }
+ return $args{_self} if $args{_self};
+
+ $args{dispatch} ||= 'Admin';
+ $args{prefix} ||= 'inc';
+ $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author');
+ $args{bundle} ||= 'inc/BUNDLES';
+ $args{base} ||= $base_path;
+ $class =~ s/^\Q$args{prefix}\E:://;
+ $args{name} ||= $class;
+ $args{version} ||= $class->VERSION;
+ unless ( $args{path} ) {
+ $args{path} = $args{name};
+ $args{path} =~ s!::!/!g;
+ }
+ $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm";
+ $args{wrote} = 0;
+
+ bless( \%args, $class );
+}
+
+sub call {
+ my ($self, $method) = @_;
+ my $obj = $self->load($method) or return;
+ splice(@_, 0, 2, $obj);
+ goto &{$obj->can($method)};
+}
+
+sub load {
+ my ($self, $method) = @_;
+
+ $self->load_extensions(
+ "$self->{prefix}/$self->{path}", $self
+ ) unless $self->{extensions};
+
+ foreach my $obj (@{$self->{extensions}}) {
+ return $obj if $obj->can($method);
+ }
+
+ my $admin = $self->{admin} or die <<"END_DIE";
+The '$method' method does not exist in the '$self->{prefix}' path!
+Please remove the '$self->{prefix}' directory and run $0 again to load it.
+END_DIE
+
+ my $obj = $admin->load($method, 1);
+ push @{$self->{extensions}}, $obj;
+
+ $obj;
+}
+
+sub load_extensions {
+ my ($self, $path, $top) = @_;
+
+ my $should_reload = 0;
+ unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) {
+ unshift @INC, $self->{prefix};
+ $should_reload = 1;
+ }
+
+ foreach my $rv ( $self->find_extensions($path) ) {
+ my ($file, $pkg) = @{$rv};
+ next if $self->{pathnames}{$pkg};
+
+ local $@;
+ my $new = eval { local $^W; require $file; $pkg->can('new') };
+ unless ( $new ) {
+ warn $@ if $@;
+ next;
+ }
+ $self->{pathnames}{$pkg} =
+ $should_reload ? delete $INC{$file} : $INC{$file};
+ push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
+ }
+
+ $self->{extensions} ||= [];
+}
+
+sub find_extensions {
+ my ($self, $path) = @_;
+
+ my @found;
+ File::Find::find( sub {
+ my $file = $File::Find::name;
+ return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
+ my $subpath = $1;
+ return if lc($subpath) eq lc($self->{dispatch});
+
+ $file = "$self->{path}/$subpath.pm";
+ my $pkg = "$self->{name}::$subpath";
+ $pkg =~ s!/!::!g;
+
+ # If we have a mixed-case package name, assume case has been preserved
+ # correctly. Otherwise, root through the file to locate the case-preserved
+ # version of the package name.
+ if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
+ my $content = Module::Install::_read($subpath . '.pm');
+ my $in_pod = 0;
+ foreach ( split //, $content ) {
+ $in_pod = 1 if /^=\w/;
+ $in_pod = 0 if /^=cut/;
+ next if ($in_pod || /^=cut/); # skip pod text
+ next if /^\s*#/; # and comments
+ if ( m/^\s*package\s+($pkg)\s*;/i ) {
+ $pkg = $1;
+ last;
+ }
+ }
+ }
+
+ push @found, [ $file, $pkg ];
+ }, $path ) if -d $path;
+
+ @found;
+}
+
+
+
+
+
+#####################################################################
+# Common Utility Functions
+
+sub _caller {
+ my $depth = 0;
+ my $call = caller($depth);
+ while ( $call eq __PACKAGE__ ) {
+ $depth++;
+ $call = caller($depth);
+ }
+ return $call;
+}
+
+# Done in evals to avoid confusing Perl::MinimumVersion
+eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
+sub _read {
+ local *FH;
+ open( FH, '<', $_[0] ) or die "open($_[0]): $!";
+ my $string = do { local $/; <FH> };
+ close FH or die "close($_[0]): $!";
+ return $string;
+}
+END_NEW
+sub _read {
+ local *FH;
+ open( FH, "< $_[0]" ) or die "open($_[0]): $!";
+ my $string = do { local $/; <FH> };
+ close FH or die "close($_[0]): $!";
+ return $string;
+}
+END_OLD
+
+sub _readperl {
+ my $string = Module::Install::_read($_[0]);
+ $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
+ $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s;
+ $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg;
+ return $string;
+}
+
+sub _readpod {
+ my $string = Module::Install::_read($_[0]);
+ $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
+ return $string if $_[0] =~ /\.pod\z/;
+ $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg;
+ $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg;
+ $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg;
+ $string =~ s/^\n+//s;
+ return $string;
+}
+
+# Done in evals to avoid confusing Perl::MinimumVersion
+eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
+sub _write {
+ local *FH;
+ open( FH, '>', $_[0] ) or die "open($_[0]): $!";
+ foreach ( 1 .. $#_ ) {
+ print FH $_[$_] or die "print($_[0]): $!";
+ }
+ close FH or die "close($_[0]): $!";
+}
+END_NEW
+sub _write {
+ local *FH;
+ open( FH, "> $_[0]" ) or die "open($_[0]): $!";
+ foreach ( 1 .. $#_ ) {
+ print FH $_[$_] or die "print($_[0]): $!";
+ }
+ close FH or die "close($_[0]): $!";
+}
+END_OLD
+
+# _version is for processing module versions (eg, 1.03_05) not
+# Perl versions (eg, 5.8.1).
+sub _version ($) {
+ my $s = shift || 0;
+ my $d =()= $s =~ /(\.)/g;
+ if ( $d >= 2 ) {
+ # Normalise multipart versions
+ $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg;
+ }
+ $s =~ s/^(\d+)\.?//;
+ my $l = $1 || 0;
+ my @v = map {
+ $_ . '0' x (3 - length $_)
+ } $s =~ /(\d{1,3})\D?/g;
+ $l = $l . '.' . join '', @v if @v;
+ return $l + 0;
+}
+
+sub _cmp ($$) {
+ _version($_[1]) <=> _version($_[2]);
+}
+
+# Cloned from Params::Util::_CLASS
+sub _CLASS ($) {
+ (
+ defined $_[0]
+ and
+ ! ref $_[0]
+ and
+ $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s
+ ) ? $_[0] : undef;
+}
+
+1;
+
+# Copyright 2008 - 2012 Adam Kennedy.
diff --git a/inc/Module/Install/Base.pm b/inc/Module/Install/Base.pm
new file mode 100644
index 0000000..802844a
--- /dev/null
+++ b/inc/Module/Install/Base.pm
@@ -0,0 +1,83 @@
+#line 1
+package Module::Install::Base;
+
+use strict 'vars';
+use vars qw{$VERSION};
+BEGIN {
+ $VERSION = '1.06';
+}
+
+# Suspend handler for "redefined" warnings
+BEGIN {
+ my $w = $SIG{__WARN__};
+ $SIG{__WARN__} = sub { $w };
+}
+
+#line 42
+
+sub new {
+ my $class = shift;
+ unless ( defined &{"${class}::call"} ) {
+ *{"${class}::call"} = sub { shift->_top->call(@_) };
+ }
+ unless ( defined &{"${class}::load"} ) {
+ *{"${class}::load"} = sub { shift->_top->load(@_) };
+ }
+ bless { @_ }, $class;
+}
+
+#line 61
+
+sub AUTOLOAD {
+ local $@;
+ my $func = eval { shift->_top->autoload } or return;
+ goto &$func;
+}
+
+#line 75
+
+sub _top {
+ $_[0]->{_top};
+}
+
+#line 90
+
+sub admin {
+ $_[0]->_top->{admin}
+ or
+ Module::Install::Base::FakeAdmin->new;
+}
+
+#line 106
+
+sub is_admin {
+ ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin');
+}
+
+sub DESTROY {}
+
+package Module::Install::Base::FakeAdmin;
+
+use vars qw{$VERSION};
+BEGIN {
+ $VERSION = $Module::Install::Base::VERSION;
+}
+
+my $fake;
+
+sub new {
+ $fake ||= bless(\@_, $_[0]);
+}
+
+sub AUTOLOAD {}
+
+sub DESTROY {}
+
+# Restore warning handler
+BEGIN {
+ $SIG{__WARN__} = $SIG{__WARN__}->();
+}
+
+1;
+
+#line 159
diff --git a/inc/Module/Install/Can.pm b/inc/Module/Install/Can.pm
new file mode 100644
index 0000000..22167b8
--- /dev/null
+++ b/inc/Module/Install/Can.pm
@@ -0,0 +1,154 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Config ();
+use ExtUtils::MakeMaker ();
+use Module::Install::Base ();
+
+use vars qw{$VERSION @ISA $ISCORE};
+BEGIN {
+ $VERSION = '1.06';
+ @ISA = 'Module::Install::Base';
+ $ISCORE = 1;
+}
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+ my ($self, $mod, $ver) = @_;
+ $mod =~ s{::|\\}{/}g;
+ $mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+ my $pkg = $mod;
+ $pkg =~ s{/}{::}g;
+ $pkg =~ s{\.pm$}{}i;
+
+ local $@;
+ eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# Check if we can run some command
+sub can_run {
+ my ($self, $cmd) = @_;
+
+ my $_cmd = $cmd;
+ return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+ for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+ next if $dir eq '';
+ require File::Spec;
+ my $abs = File::Spec->catfile($dir, $cmd);
+ return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+ }
+
+ return;
+}
+
+# Can our C compiler environment build XS files
+sub can_xs {
+ my $self = shift;
+
+ # Ensure we have the CBuilder module
+ $self->configure_requires( 'ExtUtils::CBuilder' => 0.27 );
+
+ # Do we have the configure_requires checker?
+ local $@;
+ eval "require ExtUtils::CBuilder;";
+ if ( $@ ) {
+ # They don't obey configure_requires, so it is
+ # someone old and delicate. Try to avoid hurting
+ # them by falling back to an older simpler test.
+ return $self->can_cc();
+ }
+
+ # Do we have a working C compiler
+ my $builder = ExtUtils::CBuilder->new(
+ quiet => 1,
+ );
+ unless ( $builder->have_compiler ) {
+ # No working C compiler
+ return 0;
+ }
+
+ # Write a C file representative of what XS becomes
+ require File::Temp;
+ my ( $FH, $tmpfile ) = File::Temp::tempfile(
+ "compilexs-XXXXX",
+ SUFFIX => '.c',
+ );
+ binmode $FH;
+ print $FH <<'END_C';
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+int main(int argc, char **argv) {
+ return 0;
+}
+
+int boot_sanexs() {
+ return 1;
+}
+
+END_C
+ close $FH;
+
+ # Can the C compiler access the same headers XS does
+ my @libs = ();
+ my $object = undef;
+ eval {
+ local $^W = 0;
+ $object = $builder->compile(
+ source => $tmpfile,
+ );
+ @libs = $builder->link(
+ objects => $object,
+ module_name => 'sanexs',
+ );
+ };
+ my $result = $@ ? 0 : 1;
+
+ # Clean up all the build files
+ foreach ( $tmpfile, $object, @libs ) {
+ next unless defined $_;
+ 1 while unlink;
+ }
+
+ return $result;
+}
+
+# Can we locate a (the) C compiler
+sub can_cc {
+ my $self = shift;
+ my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+ # $Config{cc} may contain args; try to find out the program part
+ while (@chunks) {
+ return $self->can_run("@chunks") || (pop(@chunks), next);
+ }
+
+ return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+ require ExtUtils::MM_Cygwin;
+ require ExtUtils::MM_Win32;
+ if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+ *ExtUtils::MM_Cygwin::maybe_command = sub {
+ my ($self, $file) = @_;
+ if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+ ExtUtils::MM_Win32->maybe_command($file);
+ } else {
+ ExtUtils::MM_Unix->maybe_command($file);
+ }
+ }
+ }
+}
+
+1;
+
+__END__
+
+#line 236
diff --git a/inc/Module/Install/Fetch.pm b/inc/Module/Install/Fetch.pm
new file mode 100644
index 0000000..bee0c4f
--- /dev/null
+++ b/inc/Module/Install/Fetch.pm
@@ -0,0 +1,93 @@
+#line 1
+package Module::Install::Fetch;
+
+use strict;
+use Module::Install::Base ();
+
+use vars qw{$VERSION @ISA $ISCORE};
+BEGIN {
+ $VERSION = '1.06';
+ @ISA = 'Module::Install::Base';
+ $ISCORE = 1;
+}
+
+sub get_file {
+ my ($self, %args) = @_;
+ my ($scheme, $host, $path, $file) =
+ $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+
+ if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
+ $args{url} = $args{ftp_url}
+ or (warn("LWP support unavailable!\n"), return);
+ ($scheme, $host, $path, $file) =
+ $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+ }
+
+ $|++;
+ print "Fetching '$file' from $host... ";
+
+ unless (eval { require Socket; Socket::inet_aton($host) }) {
+ warn "'$host' resolve failed!\n";
+ return;
+ }
+
+ return unless $scheme eq 'ftp' or $scheme eq 'http';
+
+ require Cwd;
+ my $dir = Cwd::getcwd();
+ chdir $args{local_dir} or return if exists $args{local_dir};
+
+ if (eval { require LWP::Simple; 1 }) {
+ LWP::Simple::mirror($args{url}, $file);
+ }
+ elsif (eval { require Net::FTP; 1 }) { eval {
+ # use Net::FTP to get past firewall
+ my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
+ $ftp->login("anonymous", 'anonymous@example.com');
+ $ftp->cwd($path);
+ $ftp->binary;
+ $ftp->get($file) or (warn("$!\n"), return);
+ $ftp->quit;
+ } }
+ elsif (my $ftp = $self->can_run('ftp')) { eval {
+ # no Net::FTP, fallback to ftp.exe
+ require FileHandle;
+ my $fh = FileHandle->new;
+
+ local $SIG{CHLD} = 'IGNORE';
+ unless ($fh->open("|$ftp -n")) {
+ warn "Couldn't open ftp: $!\n";
+ chdir $dir; return;
+ }
+
+ my @dialog = split(/\n/, <<"END_FTP");
+open $host
+user anonymous anonymous\@example.com
+cd $path
+binary
+get $file $file
+quit
+END_FTP
+ foreach (@dialog) { $fh->print("$_\n") }
+ $fh->close;
+ } }
+ else {
+ warn "No working 'ftp' program available!\n";
+ chdir $dir; return;
+ }
+
+ unless (-f $file) {
+ warn "Fetching failed: $@\n";
+ chdir $dir; return;
+ }
+
+ return if exists $args{size} and -s $file != $args{size};
+ system($args{run}) if exists $args{run};
+ unlink($file) if $args{remove};
+
+ print(((!exists $args{check_for} or -e $args{check_for})
+ ? "done!" : "failed! ($!)"), "\n");
+ chdir $dir; return !$?;
+}
+
+1;
diff --git a/inc/Module/Install/Makefile.pm b/inc/Module/Install/Makefile.pm
new file mode 100644
index 0000000..7052f36
--- /dev/null
+++ b/inc/Module/Install/Makefile.pm
@@ -0,0 +1,418 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use ExtUtils::MakeMaker ();
+use Module::Install::Base ();
+use Fcntl qw/:flock :seek/;
+
+use vars qw{$VERSION @ISA $ISCORE};
+BEGIN {
+ $VERSION = '1.06';
+ @ISA = 'Module::Install::Base';
+ $ISCORE = 1;
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+ shift;
+
+ # Infinite loop protection
+ my @c = caller();
+ if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+ die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+ }
+
+ # In automated testing or non-interactive session, always use defaults
+ if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+ local $ENV{PERL_MM_USE_DEFAULT} = 1;
+ goto &ExtUtils::MakeMaker::prompt;
+ } else {
+ goto &ExtUtils::MakeMaker::prompt;
+ }
+}
+
+# Store a cleaned up version of the MakeMaker version,
+# since we need to behave differently in a variety of
+# ways based on the MM version.
+my $makemaker = eval $ExtUtils::MakeMaker::VERSION;
+
+# If we are passed a param, do a "newer than" comparison.
+# Otherwise, just return the MakeMaker version.
+sub makemaker {
+ ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0
+}
+
+# Ripped from ExtUtils::MakeMaker 6.56, and slightly modified
+# as we only need to know here whether the attribute is an array
+# or a hash or something else (which may or may not be appendable).
+my %makemaker_argtype = (
+ C => 'ARRAY',
+ CONFIG => 'ARRAY',
+# CONFIGURE => 'CODE', # ignore
+ DIR => 'ARRAY',
+ DL_FUNCS => 'HASH',
+ DL_VARS => 'ARRAY',
+ EXCLUDE_EXT => 'ARRAY',
+ EXE_FILES => 'ARRAY',
+ FUNCLIST => 'ARRAY',
+ H => 'ARRAY',
+ IMPORTS => 'HASH',
+ INCLUDE_EXT => 'ARRAY',
+ LIBS => 'ARRAY', # ignore ''
+ MAN1PODS => 'HASH',
+ MAN3PODS => 'HASH',
+ META_ADD => 'HASH',
+ META_MERGE => 'HASH',
+ PL_FILES => 'HASH',
+ PM => 'HASH',
+ PMLIBDIRS => 'ARRAY',
+ PMLIBPARENTDIRS => 'ARRAY',
+ PREREQ_PM => 'HASH',
+ CONFIGURE_REQUIRES => 'HASH',
+ SKIP => 'ARRAY',
+ TYPEMAPS => 'ARRAY',
+ XS => 'HASH',
+# VERSION => ['version',''], # ignore
+# _KEEP_AFTER_FLUSH => '',
+
+ clean => 'HASH',
+ depend => 'HASH',
+ dist => 'HASH',
+ dynamic_lib=> 'HASH',
+ linkext => 'HASH',
+ macro => 'HASH',
+ postamble => 'HASH',
+ realclean => 'HASH',
+ test => 'HASH',
+ tool_autosplit => 'HASH',
+
+ # special cases where you can use makemaker_append
+ CCFLAGS => 'APPENDABLE',
+ DEFINE => 'APPENDABLE',
+ INC => 'APPENDABLE',
+ LDDLFLAGS => 'APPENDABLE',
+ LDFROM => 'APPENDABLE',
+);
+
+sub makemaker_args {
+ my ($self, %new_args) = @_;
+ my $args = ( $self->{makemaker_args} ||= {} );
+ foreach my $key (keys %new_args) {
+ if ($makemaker_argtype{$key}) {
+ if ($makemaker_argtype{$key} eq 'ARRAY') {
+ $args->{$key} = [] unless defined $args->{$key};
+ unless (ref $args->{$key} eq 'ARRAY') {
+ $args->{$key} = [$args->{$key}]
+ }
+ push @{$args->{$key}},
+ ref $new_args{$key} eq 'ARRAY'
+ ? @{$new_args{$key}}
+ : $new_args{$key};
+ }
+ elsif ($makemaker_argtype{$key} eq 'HASH') {
+ $args->{$key} = {} unless defined $args->{$key};
+ foreach my $skey (keys %{ $new_args{$key} }) {
+ $args->{$key}{$skey} = $new_args{$key}{$skey};
+ }
+ }
+ elsif ($makemaker_argtype{$key} eq 'APPENDABLE') {
+ $self->makemaker_append($key => $new_args{$key});
+ }
+ }
+ else {
+ if (defined $args->{$key}) {
+ warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n};
+ }
+ $args->{$key} = $new_args{$key};
+ }
+ }
+ return $args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+ my $self = shift;
+ my $name = shift;
+ my $args = $self->makemaker_args;
+ $args->{$name} = defined $args->{$name}
+ ? join( ' ', $args->{$name}, @_ )
+ : join( ' ', @_ );
+}
+
+sub build_subdirs {
+ my $self = shift;
+ my $subdirs = $self->makemaker_args->{DIR} ||= [];
+ for my $subdir (@_) {
+ push @$subdirs, $subdir;
+ }
+}
+
+sub clean_files {
+ my $self = shift;
+ my $clean = $self->makemaker_args->{clean} ||= {};
+ %$clean = (
+ %$clean,
+ FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_),
+ );
+}
+
+sub realclean_files {
+ my $self = shift;
+ my $realclean = $self->makemaker_args->{realclean} ||= {};
+ %$realclean = (
+ %$realclean,
+ FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_),
+ );
+}
+
+sub libs {
+ my $self = shift;
+ my $libs = ref $_[0] ? shift : [ shift ];
+ $self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+ my $self = shift;
+ $self->makemaker_args( INC => shift );
+}
+
+sub _wanted_t {
+}
+
+sub tests_recursive {
+ my $self = shift;
+ my $dir = shift || 't';
+ unless ( -d $dir ) {
+ die "tests_recursive dir '$dir' does not exist";
+ }
+ my %tests = map { $_ => 1 } split / /, ($self->tests || '');
+ require File::Find;
+ File::Find::find(
+ sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 },
+ $dir
+ );
+ $self->tests( join ' ', sort keys %tests );
+}
+
+sub write {
+ my $self = shift;
+ die "&Makefile->write() takes no arguments\n" if @_;
+
+ # Check the current Perl version
+ my $perl_version = $self->perl_version;
+ if ( $perl_version ) {
+ eval "use $perl_version; 1"
+ or die "ERROR: perl: Version $] is installed, "
+ . "but we need version >= $perl_version";
+ }
+
+ # Make sure we have a new enough MakeMaker
+ require ExtUtils::MakeMaker;
+
+ if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) {
+ # This previous attempted to inherit the version of
+ # ExtUtils::MakeMaker in use by the module author, but this
+ # was found to be untenable as some authors build releases
+ # using future dev versions of EU:MM that nobody else has.
+ # Instead, #toolchain suggests we use 6.59 which is the most
+ # stable version on CPAN at time of writing and is, to quote
+ # ribasushi, "not terminally fucked, > and tested enough".
+ # TODO: We will now need to maintain this over time to push
+ # the version up as new versions are released.
+ $self->build_requires( 'ExtUtils::MakeMaker' => 6.59 );
+ $self->configure_requires( 'ExtUtils::MakeMaker' => 6.59 );
+ } else {
+ # Allow legacy-compatibility with 5.005 by depending on the
+ # most recent EU:MM that supported 5.005.
+ $self->build_requires( 'ExtUtils::MakeMaker' => 6.36 );
+ $self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 );
+ }
+
+ # Generate the MakeMaker params
+ my $args = $self->makemaker_args;
+ $args->{DISTNAME} = $self->name;
+ $args->{NAME} = $self->module_name || $self->name;
+ $args->{NAME} =~ s/-/::/g;
+ $args->{VERSION} = $self->version or die <<'EOT';
+ERROR: Can't determine distribution version. Please specify it
+explicitly via 'version' in Makefile.PL, or set a valid $VERSION
+in a module, and provide its file path via 'version_from' (or
+'all_from' if you prefer) in Makefile.PL.
+EOT
+
+ if ( $self->tests ) {
+ my @tests = split ' ', $self->tests;
+ my %seen;
+ $args->{test} = {
+ TESTS => (join ' ', grep {!$seen{$_}++} @tests),
+ };
+ } elsif ( $Module::Install::ExtraTests::use_extratests ) {
+ # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness.
+ # So, just ignore our xt tests here.
+ } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) {
+ $args->{test} = {
+ TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ),
+ };
+ }
+ if ( $] >= 5.005 ) {
+ $args->{ABSTRACT} = $self->abstract;
+ $args->{AUTHOR} = join ', ', @{$self->author || []};
+ }
+ if ( $self->makemaker(6.10) ) {
+ $args->{NO_META} = 1;
+ #$args->{NO_MYMETA} = 1;
+ }
+ if ( $self->makemaker(6.17) and $self->sign ) {
+ $args->{SIGN} = 1;
+ }
+ unless ( $self->is_admin ) {
+ delete $args->{SIGN};
+ }
+ if ( $self->makemaker(6.31) and $self->license ) {
+ $args->{LICENSE} = $self->license;
+ }
+
+ my $prereq = ($args->{PREREQ_PM} ||= {});
+ %$prereq = ( %$prereq,
+ map { @$_ } # flatten [module => version]
+ map { @$_ }
+ grep $_,
+ ($self->requires)
+ );
+
+ # Remove any reference to perl, PREREQ_PM doesn't support it
+ delete $args->{PREREQ_PM}->{perl};
+
+ # Merge both kinds of requires into BUILD_REQUIRES
+ my $build_prereq = ($args->{BUILD_REQUIRES} ||= {});
+ %$build_prereq = ( %$build_prereq,
+ map { @$_ } # flatten [module => version]
+ map { @$_ }
+ grep $_,
+ ($self->configure_requires, $self->build_requires)
+ );
+
+ # Remove any reference to perl, BUILD_REQUIRES doesn't support it
+ delete $args->{BUILD_REQUIRES}->{perl};
+
+ # Delete bundled dists from prereq_pm, add it to Makefile DIR
+ my $subdirs = ($args->{DIR} || []);
+ if ($self->bundles) {
+ my %processed;
+ foreach my $bundle (@{ $self->bundles }) {
+ my ($mod_name, $dist_dir) = @$bundle;
+ delete $prereq->{$mod_name};
+ $dist_dir = File::Basename::basename($dist_dir); # dir for building this module
+ if (not exists $processed{$dist_dir}) {
+ if (-d $dist_dir) {
+ # List as sub-directory to be processed by make
+ push @$subdirs, $dist_dir;
+ }
+ # Else do nothing: the module is already present on the system
+ $processed{$dist_dir} = undef;
+ }
+ }
+ }
+
+ unless ( $self->makemaker('6.55_03') ) {
+ %$prereq = (%$prereq,%$build_prereq);
+ delete $args->{BUILD_REQUIRES};
+ }
+
+ if ( my $perl_version = $self->perl_version ) {
+ eval "use $perl_version; 1"
+ or die "ERROR: perl: Version $] is installed, "
+ . "but we need version >= $perl_version";
+
+ if ( $self->makemaker(6.48) ) {
+ $args->{MIN_PERL_VERSION} = $perl_version;
+ }
+ }
+
+ if ($self->installdirs) {
+ warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS};
+ $args->{INSTALLDIRS} = $self->installdirs;
+ }
+
+ my %args = map {
+ ( $_ => $args->{$_} ) } grep {defined($args->{$_} )
+ } keys %$args;
+
+ my $user_preop = delete $args{dist}->{PREOP};
+ if ( my $preop = $self->admin->preop($user_preop) ) {
+ foreach my $key ( keys %$preop ) {
+ $args{dist}->{$key} = $preop->{$key};
+ }
+ }
+
+ my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+ $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+ my $self = shift;
+ my $makefile_name = shift;
+ my $top_class = ref($self->_top) || '';
+ my $top_version = $self->_top->VERSION || '';
+
+ my $preamble = $self->preamble
+ ? "# Preamble by $top_class $top_version\n"
+ . $self->preamble
+ : '';
+ my $postamble = "# Postamble by $top_class $top_version\n"
+ . ($self->postamble || '');
+
+ local *MAKEFILE;
+ open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+ eval { flock MAKEFILE, LOCK_EX };
+ my $makefile = do { local $/; <MAKEFILE> };
+
+ $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+ $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+ $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+ $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+ $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+ # Module::Install will never be used to build the Core Perl
+ # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+ # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+ $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+ #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+ # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+ $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g;
+
+ # XXX - This is currently unused; not sure if it breaks other MM-users
+ # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+ seek MAKEFILE, 0, SEEK_SET;
+ truncate MAKEFILE, 0;
+ print MAKEFILE "$preamble$makefile$postamble" or die $!;
+ close MAKEFILE or die $!;
+
+ 1;
+}
+
+sub preamble {
+ my ($self, $text) = @_;
+ $self->{preamble} = $text . $self->{preamble} if defined $text;
+ $self->{preamble};
+}
+
+sub postamble {
+ my ($self, $text) = @_;
+ $self->{postamble} ||= $self->admin->postamble;
+ $self->{postamble} .= $text if defined $text;
+ $self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 544
diff --git a/inc/Module/Install/Metadata.pm b/inc/Module/Install/Metadata.pm
new file mode 100644
index 0000000..58430f3
--- /dev/null
+++ b/inc/Module/Install/Metadata.pm
@@ -0,0 +1,722 @@
+#line 1
+package Module::Install::Metadata;
+
+use strict 'vars';
+use Module::Install::Base ();
+
+use vars qw{$VERSION @ISA $ISCORE};
+BEGIN {
+ $VERSION = '1.06';
+ @ISA = 'Module::Install::Base';
+ $ISCORE = 1;
+}
+
+my @boolean_keys = qw{
+ sign
+};
+
+my @scalar_keys = qw{
+ name
+ module_name
+ abstract
+ version
+ distribution_type
+ tests
+ installdirs
+};
+
+my @tuple_keys = qw{
+ configure_requires
+ build_requires
+ requires
+ recommends
+ bundles
+ resources
+};
+
+my @resource_keys = qw{
+ homepage
+ bugtracker
+ repository
+};
+
+my @array_keys = qw{
+ keywords
+ author
+};
+
+*authors = \&author;
+
+sub Meta { shift }
+sub Meta_BooleanKeys { @boolean_keys }
+sub Meta_ScalarKeys { @scalar_keys }
+sub Meta_TupleKeys { @tuple_keys }
+sub Meta_ResourceKeys { @resource_keys }
+sub Meta_ArrayKeys { @array_keys }
+
+foreach my $key ( @boolean_keys ) {
+ *$key = sub {
+ my $self = shift;
+ if ( defined wantarray and not @_ ) {
+ return $self->{values}->{$key};
+ }
+ $self->{values}->{$key} = ( @_ ? $_[0] : 1 );
+ return $self;
+ };
+}
+
+foreach my $key ( @scalar_keys ) {
+ *$key = sub {
+ my $self = shift;
+ return $self->{values}->{$key} if defined wantarray and !@_;
+ $self->{values}->{$key} = shift;
+ return $self;
+ };
+}
+
+foreach my $key ( @array_keys ) {
+ *$key = sub {
+ my $self = shift;
+ return $self->{values}->{$key} if defined wantarray and !@_;
+ $self->{values}->{$key} ||= [];
+ push @{$self->{values}->{$key}}, @_;
+ return $self;
+ };
+}
+
+foreach my $key ( @resource_keys ) {
+ *$key = sub {
+ my $self = shift;
+ unless ( @_ ) {
+ return () unless $self->{values}->{resources};
+ return map { $_->[1] }
+ grep { $_->[0] eq $key }
+ @{ $self->{values}->{resources} };
+ }
+ return $self->{values}->{resources}->{$key} unless @_;
+ my $uri = shift or die(
+ "Did not provide a value to $key()"
+ );
+ $self->resources( $key => $uri );
+ return 1;
+ };
+}
+
+foreach my $key ( grep { $_ ne "resources" } @tuple_keys) {
+ *$key = sub {
+ my $self = shift;
+ return $self->{values}->{$key} unless @_;
+ my @added;
+ while ( @_ ) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ push @added, [ $module, $version ];
+ }
+ push @{ $self->{values}->{$key} }, @added;
+ return map {@$_} @added;
+ };
+}
+
+# Resource handling
+my %lc_resource = map { $_ => 1 } qw{
+ homepage
+ license
+ bugtracker
+ repository
+};
+
+sub resources {
+ my $self = shift;
+ while ( @_ ) {
+ my $name = shift or last;
+ my $value = shift or next;
+ if ( $name eq lc $name and ! $lc_resource{$name} ) {
+ die("Unsupported reserved lowercase resource '$name'");
+ }
+ $self->{values}->{resources} ||= [];
+ push @{ $self->{values}->{resources} }, [ $name, $value ];
+ }
+ $self->{values}->{resources};
+}
+
+# Aliases for build_requires that will have alternative
+# meanings in some future version of META.yml.
+sub test_requires { shift->build_requires(@_) }
+sub install_requires { shift->build_requires(@_) }
+
+# Aliases for installdirs options
+sub install_as_core { $_[0]->installdirs('perl') }
+sub install_as_cpan { $_[0]->installdirs('site') }
+sub install_as_site { $_[0]->installdirs('site') }
+sub install_as_vendor { $_[0]->installdirs('vendor') }
+
+sub dynamic_config {
+ my $self = shift;
+ my $value = @_ ? shift : 1;
+ if ( $self->{values}->{dynamic_config} ) {
+ # Once dynamic we never change to static, for safety
+ return 0;
+ }
+ $self->{values}->{dynamic_config} = $value ? 1 : 0;
+ return 1;
+}
+
+# Convenience command
+sub static_config {
+ shift->dynamic_config(0);
+}
+
+sub perl_version {
+ my $self = shift;
+ return $self->{values}->{perl_version} unless @_;
+ my $version = shift or die(
+ "Did not provide a value to perl_version()"
+ );
+
+ # Normalize the version
+ $version = $self->_perl_version($version);
+
+ # We don't support the really old versions
+ unless ( $version >= 5.005 ) {
+ die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n";
+ }
+
+ $self->{values}->{perl_version} = $version;
+}
+
+sub all_from {
+ my ( $self, $file ) = @_;
+
+ unless ( defined($file) ) {
+ my $name = $self->name or die(
+ "all_from called with no args without setting name() first"
+ );
+ $file = join('/', 'lib', split(/-/, $name)) . '.pm';
+ $file =~ s{.*/}{} unless -e $file;
+ unless ( -e $file ) {
+ die("all_from cannot find $file from $name");
+ }
+ }
+ unless ( -f $file ) {
+ die("The path '$file' does not exist, or is not a file");
+ }
+
+ $self->{values}{all_from} = $file;
+
+ # Some methods pull from POD instead of code.
+ # If there is a matching .pod, use that instead
+ my $pod = $file;
+ $pod =~ s/\.pm$/.pod/i;
+ $pod = $file unless -e $pod;
+
+ # Pull the different values
+ $self->name_from($file) unless $self->name;
+ $self->version_from($file) unless $self->version;
+ $self->perl_version_from($file) unless $self->perl_version;
+ $self->author_from($pod) unless @{$self->author || []};
+ $self->license_from($pod) unless $self->license;
+ $self->abstract_from($pod) unless $self->abstract;
+
+ return 1;
+}
+
+sub provides {
+ my $self = shift;
+ my $provides = ( $self->{values}->{provides} ||= {} );
+ %$provides = (%$provides, @_) if @_;
+ return $provides;
+}
+
+sub auto_provides {
+ my $self = shift;
+ return $self unless $self->is_admin;
+ unless (-e 'MANIFEST') {
+ warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
+ return $self;
+ }
+ # Avoid spurious warnings as we are not checking manifest here.
+ local $SIG{__WARN__} = sub {1};
+ require ExtUtils::Manifest;
+ local *ExtUtils::Manifest::manicheck = sub { return };
+
+ require Module::Build;
+ my $build = Module::Build->new(
+ dist_name => $self->name,
+ dist_version => $self->version,
+ license => $self->license,
+ );
+ $self->provides( %{ $build->find_dist_packages || {} } );
+}
+
+sub feature {
+ my $self = shift;
+ my $name = shift;
+ my $features = ( $self->{values}->{features} ||= [] );
+ my $mods;
+
+ if ( @_ == 1 and ref( $_[0] ) ) {
+ # The user used ->feature like ->features by passing in the second
+ # argument as a reference. Accomodate for that.
+ $mods = $_[0];
+ } else {
+ $mods = \@_;
+ }
+
+ my $count = 0;
+ push @$features, (
+ $name => [
+ map {
+ ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_
+ } @$mods
+ ]
+ );
+
+ return @$features;
+}
+
+sub features {
+ my $self = shift;
+ while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
+ $self->feature( $name, @$mods );
+ }
+ return $self->{values}->{features}
+ ? @{ $self->{values}->{features} }
+ : ();
+}
+
+sub no_index {
+ my $self = shift;
+ my $type = shift;
+ push @{ $self->{values}->{no_index}->{$type} }, @_ if $type;
+ return $self->{values}->{no_index};
+}
+
+sub read {
+ my $self = shift;
+ $self->include_deps( 'YAML::Tiny', 0 );
+
+ require YAML::Tiny;
+ my $data = YAML::Tiny::LoadFile('META.yml');
+
+ # Call methods explicitly in case user has already set some values.
+ while ( my ( $key, $value ) = each %$data ) {
+ next unless $self->can($key);
+ if ( ref $value eq 'HASH' ) {
+ while ( my ( $module, $version ) = each %$value ) {
+ $self->can($key)->($self, $module => $version );
+ }
+ } else {
+ $self->can($key)->($self, $value);
+ }
+ }
+ return $self;
+}
+
+sub write {
+ my $self = shift;
+ return $self unless $self->is_admin;
+ $self->admin->write_meta;
+ return $self;
+}
+
+sub version_from {
+ require ExtUtils::MM_Unix;
+ my ( $self, $file ) = @_;
+ $self->version( ExtUtils::MM_Unix->parse_version($file) );
+
+ # for version integrity check
+ $self->makemaker_args( VERSION_FROM => $file );
+}
+
+sub abstract_from {
+ require ExtUtils::MM_Unix;
+ my ( $self, $file ) = @_;
+ $self->abstract(
+ bless(
+ { DISTNAME => $self->name },
+ 'ExtUtils::MM_Unix'
+ )->parse_abstract($file)
+ );
+}
+
+# Add both distribution and module name
+sub name_from {
+ my ($self, $file) = @_;
+ if (
+ Module::Install::_read($file) =~ m/
+ ^ \s*
+ package \s*
+ ([\w:]+)
+ \s* ;
+ /ixms
+ ) {
+ my ($name, $module_name) = ($1, $1);
+ $name =~ s{::}{-}g;
+ $self->name($name);
+ unless ( $self->module_name ) {
+ $self->module_name($module_name);
+ }
+ } else {
+ die("Cannot determine name from $file\n");
+ }
+}
+
+sub _extract_perl_version {
+ if (
+ $_[0] =~ m/
+ ^\s*
+ (?:use|require) \s*
+ v?
+ ([\d_\.]+)
+ \s* ;
+ /ixms
+ ) {
+ my $perl_version = $1;
+ $perl_version =~ s{_}{}g;
+ return $perl_version;
+ } else {
+ return;
+ }
+}
+
+sub perl_version_from {
+ my $self = shift;
+ my $perl_version=_extract_perl_version(Module::Install::_read($_[0]));
+ if ($perl_version) {
+ $self->perl_version($perl_version);
+ } else {
+ warn "Cannot determine perl version info from $_[0]\n";
+ return;
+ }
+}
+
+sub author_from {
+ my $self = shift;
+ my $content = Module::Install::_read($_[0]);
+ if ($content =~ m/
+ =head \d \s+ (?:authors?)\b \s*
+ ([^\n]*)
+ |
+ =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
+ .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
+ ([^\n]*)
+ /ixms) {
+ my $author = $1 || $2;
+
+ # XXX: ugly but should work anyway...
+ if (eval "require Pod::Escapes; 1") {
+ # Pod::Escapes has a mapping table.
+ # It's in core of perl >= 5.9.3, and should be installed
+ # as one of the Pod::Simple's prereqs, which is a prereq
+ # of Pod::Text 3.x (see also below).
+ $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> }
+ {
+ defined $2
+ ? chr($2)
+ : defined $Pod::Escapes::Name2character_number{$1}
+ ? chr($Pod::Escapes::Name2character_number{$1})
+ : do {
+ warn "Unknown escape: E<$1>";
+ "E<$1>";
+ };
+ }gex;
+ }
+ elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) {
+ # Pod::Text < 3.0 has yet another mapping table,
+ # though the table name of 2.x and 1.x are different.
+ # (1.x is in core of Perl < 5.6, 2.x is in core of
+ # Perl < 5.9.3)
+ my $mapping = ($Pod::Text::VERSION < 2)
+ ? \%Pod::Text::HTML_Escapes
+ : \%Pod::Text::ESCAPES;
+ $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> }
+ {
+ defined $2
+ ? chr($2)
+ : defined $mapping->{$1}
+ ? $mapping->{$1}
+ : do {
+ warn "Unknown escape: E<$1>";
+ "E<$1>";
+ };
+ }gex;
+ }
+ else {
+ $author =~ s{E<lt>}{<}g;
+ $author =~ s{E<gt>}{>}g;
+ }
+ $self->author($author);
+ } else {
+ warn "Cannot determine author info from $_[0]\n";
+ }
+}
+
+#Stolen from M::B
+my %license_urls = (
+ perl => 'http://dev.perl.org/licenses/',
+ apache => 'http://apache.org/licenses/LICENSE-2.0',
+ apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1',
+ artistic => 'http://opensource.org/licenses/artistic-license.php',
+ artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php',
+ lgpl => 'http://opensource.org/licenses/lgpl-license.php',
+ lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php',
+ lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html',
+ bsd => 'http://opensource.org/licenses/bsd-license.php',
+ gpl => 'http://opensource.org/licenses/gpl-license.php',
+ gpl2 => 'http://opensource.org/licenses/gpl-2.0.php',
+ gpl3 => 'http://opensource.org/licenses/gpl-3.0.html',
+ mit => 'http://opensource.org/licenses/mit-license.php',
+ mozilla => 'http://opensource.org/licenses/mozilla1.1.php',
+ open_source => undef,
+ unrestricted => undef,
+ restrictive => undef,
+ unknown => undef,
+);
+
+sub license {
+ my $self = shift;
+ return $self->{values}->{license} unless @_;
+ my $license = shift or die(
+ 'Did not provide a value to license()'
+ );
+ $license = __extract_license($license) || lc $license;
+ $self->{values}->{license} = $license;
+
+ # Automatically fill in license URLs
+ if ( $license_urls{$license} ) {
+ $self->resources( license => $license_urls{$license} );
+ }
+
+ return 1;
+}
+
+sub _extract_license {
+ my $pod = shift;
+ my $matched;
+ return __extract_license(
+ ($matched) = $pod =~ m/
+ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?)
+ (=head \d.*|=cut.*|)\z
+ /xms
+ ) || __extract_license(
+ ($matched) = $pod =~ m/
+ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?)
+ (=head \d.*|=cut.*|)\z
+ /xms
+ );
+}
+
+sub __extract_license {
+ my $license_text = shift or return;
+ my @phrases = (
+ '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1,
+ '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1,
+ 'Artistic and GPL' => 'perl', 1,
+ 'GNU general public license' => 'gpl', 1,
+ 'GNU public license' => 'gpl', 1,
+ 'GNU lesser general public license' => 'lgpl', 1,
+ 'GNU lesser public license' => 'lgpl', 1,
+ 'GNU library general public license' => 'lgpl', 1,
+ 'GNU library public license' => 'lgpl', 1,
+ 'GNU Free Documentation license' => 'unrestricted', 1,
+ 'GNU Affero General Public License' => 'open_source', 1,
+ '(?:Free)?BSD license' => 'bsd', 1,
+ 'Artistic license 2\.0' => 'artistic_2', 1,
+ 'Artistic license' => 'artistic', 1,
+ 'Apache (?:Software )?license' => 'apache', 1,
+ 'GPL' => 'gpl', 1,
+ 'LGPL' => 'lgpl', 1,
+ 'BSD' => 'bsd', 1,
+ 'Artistic' => 'artistic', 1,
+ 'MIT' => 'mit', 1,
+ 'Mozilla Public License' => 'mozilla', 1,
+ 'Q Public License' => 'open_source', 1,
+ 'OpenSSL License' => 'unrestricted', 1,
+ 'SSLeay License' => 'unrestricted', 1,
+ 'zlib License' => 'open_source', 1,
+ 'proprietary' => 'proprietary', 0,
+ );
+ while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
+ $pattern =~ s#\s+#\\s+#gs;
+ if ( $license_text =~ /\b$pattern\b/i ) {
+ return $license;
+ }
+ }
+ return '';
+}
+
+sub license_from {
+ my $self = shift;
+ if (my $license=_extract_license(Module::Install::_read($_[0]))) {
+ $self->license($license);
+ } else {
+ warn "Cannot determine license info from $_[0]\n";
+ return 'unknown';
+ }
+}
+
+sub _extract_bugtracker {
+ my @links = $_[0] =~ m#L<(
+ https?\Q://rt.cpan.org/\E[^>]+|
+ https?\Q://github.com/\E[\w_]+/[\w_]+/issues|
+ https?\Q://code.google.com/p/\E[\w_\-]+/issues/list
+ )>#gx;
+ my %links;
+ @links{@links}=();
+ @links=keys %links;
+ return @links;
+}
+
+sub bugtracker_from {
+ my $self = shift;
+ my $content = Module::Install::_read($_[0]);
+ my @links = _extract_bugtracker($content);
+ unless ( @links ) {
+ warn "Cannot determine bugtracker info from $_[0]\n";
+ return 0;
+ }
+ if ( @links > 1 ) {
+ warn "Found more than one bugtracker link in $_[0]\n";
+ return 0;
+ }
+
+ # Set the bugtracker
+ bugtracker( $links[0] );
+ return 1;
+}
+
+sub requires_from {
+ my $self = shift;
+ my $content = Module::Install::_readperl($_[0]);
+ my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg;
+ while ( @requires ) {
+ my $module = shift @requires;
+ my $version = shift @requires;
+ $self->requires( $module => $version );
+ }
+}
+
+sub test_requires_from {
+ my $self = shift;
+ my $content = Module::Install::_readperl($_[0]);
+ my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg;
+ while ( @requires ) {
+ my $module = shift @requires;
+ my $version = shift @requires;
+ $self->test_requires( $module => $version );
+ }
+}
+
+# Convert triple-part versions (eg, 5.6.1 or 5.8.9) to
+# numbers (eg, 5.006001 or 5.008009).
+# Also, convert double-part versions (eg, 5.8)
+sub _perl_version {
+ my $v = $_[-1];
+ $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e;
+ $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e;
+ $v =~ s/(\.\d\d\d)000$/$1/;
+ $v =~ s/_.+$//;
+ if ( ref($v) ) {
+ # Numify
+ $v = $v + 0;
+ }
+ return $v;
+}
+
+sub add_metadata {
+ my $self = shift;
+ my %hash = @_;
+ for my $key (keys %hash) {
+ warn "add_metadata: $key is not prefixed with 'x_'.\n" .
+ "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/;
+ $self->{values}->{$key} = $hash{$key};
+ }
+}
+
+
+######################################################################
+# MYMETA Support
+
+sub WriteMyMeta {
+ die "WriteMyMeta has been deprecated";
+}
+
+sub write_mymeta_yaml {
+ my $self = shift;
+
+ # We need YAML::Tiny to write the MYMETA.yml file
+ unless ( eval { require YAML::Tiny; 1; } ) {
+ return 1;
+ }
+
+ # Generate the data
+ my $meta = $self->_write_mymeta_data or return 1;
+
+ # Save as the MYMETA.yml file
+ print "Writing MYMETA.yml\n";
+ YAML::Tiny::DumpFile('MYMETA.yml', $meta);
+}
+
+sub write_mymeta_json {
+ my $self = shift;
+
+ # We need JSON to write the MYMETA.json file
+ unless ( eval { require JSON; 1; } ) {
+ return 1;
+ }
+
+ # Generate the data
+ my $meta = $self->_write_mymeta_data or return 1;
+
+ # Save as the MYMETA.yml file
+ print "Writing MYMETA.json\n";
+ Module::Install::_write(
+ 'MYMETA.json',
+ JSON->new->pretty(1)->canonical->encode($meta),
+ );
+}
+
+sub _write_mymeta_data {
+ my $self = shift;
+
+ # If there's no existing META.yml there is nothing we can do
+ return undef unless -f 'META.yml';
+
+ # We need Parse::CPAN::Meta to load the file
+ unless ( eval { require Parse::CPAN::Meta; 1; } ) {
+ return undef;
+ }
+
+ # Merge the perl version into the dependencies
+ my $val = $self->Meta->{values};
+ my $perl = delete $val->{perl_version};
+ if ( $perl ) {
+ $val->{requires} ||= [];
+ my $requires = $val->{requires};
+
+ # Canonize to three-dot version after Perl 5.6
+ if ( $perl >= 5.006 ) {
+ $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e
+ }
+ unshift @$requires, [ perl => $perl ];
+ }
+
+ # Load the advisory META.yml file
+ my @yaml = Parse::CPAN::Meta::LoadFile('META.yml');
+ my $meta = $yaml[0];
+
+ # Overwrite the non-configure dependency hashs
+ delete $meta->{requires};
+ delete $meta->{build_requires};
+ delete $meta->{recommends};
+ if ( exists $val->{requires} ) {
+ $meta->{requires} = { map { @$_ } @{ $val->{requires} } };
+ }
+ if ( exists $val->{build_requires} ) {
+ $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } };
+ }
+
+ return $meta;
+}
+
+1;
diff --git a/inc/Module/Install/Win32.pm b/inc/Module/Install/Win32.pm
new file mode 100644
index 0000000..eeaa3fe
--- /dev/null
+++ b/inc/Module/Install/Win32.pm
@@ -0,0 +1,64 @@
+#line 1
+package Module::Install::Win32;
+
+use strict;
+use Module::Install::Base ();
+
+use vars qw{$VERSION @ISA $ISCORE};
+BEGIN {
+ $VERSION = '1.06';
+ @ISA = 'Module::Install::Base';
+ $ISCORE = 1;
+}
+
+# determine if the user needs nmake, and download it if needed
+sub check_nmake {
+ my $self = shift;
+ $self->load('can_run');
+ $self->load('get_file');
+
+ require Config;
+ return unless (
+ $^O eq 'MSWin32' and
+ $Config::Config{make} and
+ $Config::Config{make} =~ /^nmake\b/i and
+ ! $self->can_run('nmake')
+ );
+
+ print "The required 'nmake' executable not found, fetching it...\n";
+
+ require File::Basename;
+ my $rv = $self->get_file(
+ url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
+ ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
+ local_dir => File::Basename::dirname($^X),
+ size => 51928,
+ run => 'Nmake15.exe /o > nul',
+ check_for => 'Nmake.exe',
+ remove => 1,
+ );
+
+ die <<'END_MESSAGE' unless $rv;
+
+-------------------------------------------------------------------------------
+
+Since you are using Microsoft Windows, you will need the 'nmake' utility
+before installation. It's available at:
+
+ http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+ or
+ ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
+
+Please download the file manually, save it to a directory in %PATH% (e.g.
+C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
+that directory, and run "Nmake15.exe" from there; that will create the
+'nmake.exe' file needed by this module.
+
+You may then resume the installation process described in README.
+
+-------------------------------------------------------------------------------
+END_MESSAGE
+
+}
+
+1;
diff --git a/inc/Module/Install/WriteAll.pm b/inc/Module/Install/WriteAll.pm
new file mode 100644
index 0000000..85d8018
--- /dev/null
+++ b/inc/Module/Install/WriteAll.pm
@@ -0,0 +1,63 @@
+#line 1
+package Module::Install::WriteAll;
+
+use strict;
+use Module::Install::Base ();
+
+use vars qw{$VERSION @ISA $ISCORE};
+BEGIN {
+ $VERSION = '1.06';
+ @ISA = qw{Module::Install::Base};
+ $ISCORE = 1;
+}
+
+sub WriteAll {
+ my $self = shift;
+ my %args = (
+ meta => 1,
+ sign => 0,
+ inline => 0,
+ check_nmake => 1,
+ @_,
+ );
+
+ $self->sign(1) if $args{sign};
+ $self->admin->WriteAll(%args) if $self->is_admin;
+
+ $self->check_nmake if $args{check_nmake};
+ unless ( $self->makemaker_args->{PL_FILES} ) {
+ # XXX: This still may be a bit over-defensive...
+ unless ($self->makemaker(6.25)) {
+ $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL';
+ }
+ }
+
+ # Until ExtUtils::MakeMaker support MYMETA.yml, make sure
+ # we clean it up properly ourself.
+ $self->realclean_files('MYMETA.yml');
+
+ if ( $args{inline} ) {
+ $self->Inline->write;
+ } else {
+ $self->Makefile->write;
+ }
+
+ # The Makefile write process adds a couple of dependencies,
+ # so write the META.yml files after the Makefile.
+ if ( $args{meta} ) {
+ $self->Meta->write;
+ }
+
+ # Experimental support for MYMETA
+ if ( $ENV{X_MYMETA} ) {
+ if ( $ENV{X_MYMETA} eq 'JSON' ) {
+ $self->Meta->write_mymeta_json;
+ } else {
+ $self->Meta->write_mymeta_yaml;
+ }
+ }
+
+ return 1;
+}
+
+1;
diff --git a/lib/File/HomeDir.pm b/lib/File/HomeDir.pm
new file mode 100644
index 0000000..624e9f2
--- /dev/null
+++ b/lib/File/HomeDir.pm
@@ -0,0 +1,720 @@
+package File::HomeDir;
+
+# See POD at end for documentation
+
+use 5.00503;
+use strict;
+use Carp ();
+use Config ();
+use File::Spec ();
+use File::Which ();
+
+# Globals
+use vars qw{$VERSION @ISA @EXPORT @EXPORT_OK $IMPLEMENTED_BY};
+BEGIN {
+ $VERSION = '1.00';
+
+ # Inherit manually
+ require Exporter;
+ @ISA = qw{ Exporter };
+ @EXPORT = qw{ home };
+ @EXPORT_OK = qw{
+ home
+ my_home
+ my_desktop
+ my_documents
+ my_music
+ my_pictures
+ my_videos
+ my_data
+ my_dist_config
+ my_dist_data
+ users_home
+ users_desktop
+ users_documents
+ users_music
+ users_pictures
+ users_videos
+ users_data
+ };
+
+ # %~ doesn't need (and won't take) exporting, as it's a magic
+ # symbol name that's always looked for in package 'main'.
+}
+
+# Inlined Params::Util functions
+sub _CLASS ($) {
+ (defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s) ? $_[0] : undef;
+}
+sub _DRIVER ($$) {
+ (defined _CLASS($_[0]) and eval "require $_[0];" and ! $@ and $_[0]->isa($_[1]) and $_[0] ne $_[1]) ? $_[0] : undef;
+}
+
+# Platform detection
+if ( $IMPLEMENTED_BY ) {
+ # Allow for custom HomeDir classes
+ # Leave it as the existing value
+} elsif ( $^O eq 'MSWin32' ) {
+ # All versions of Windows
+ $IMPLEMENTED_BY = 'File::HomeDir::Windows';
+} elsif ( $^O eq 'darwin') {
+ # 1st: try Mac::SystemDirectory by chansen
+ if ( eval { require Mac::SystemDirectory; 1 } ) {
+ $IMPLEMENTED_BY = 'File::HomeDir::Darwin::Cocoa';
+ } elsif ( eval { require Mac::Files; 1 } ) {
+ # 2nd try Mac::Files: Carbon - unmaintained since 2006 except some 64bit fixes
+ $IMPLEMENTED_BY = 'File::HomeDir::Darwin::Carbon';
+ } else {
+ # 3rd: fallback: pure perl
+ $IMPLEMENTED_BY = 'File::HomeDir::Darwin';
+ }
+} elsif ( $^O eq 'MacOS' ) {
+ # Legacy Mac OS
+ $IMPLEMENTED_BY = 'File::HomeDir::MacOS9';
+} elsif ( File::Which::which('xdg-user-dir') ) {
+ # freedesktop unixes
+ $IMPLEMENTED_BY = 'File::HomeDir::FreeDesktop';
+} else {
+ # Default to Unix semantics
+ $IMPLEMENTED_BY = 'File::HomeDir::Unix';
+}
+unless ( _DRIVER($IMPLEMENTED_BY, 'File::HomeDir::Driver') ) {
+ Carp::croak("Missing or invalid File::HomeDir driver $IMPLEMENTED_BY");
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ $IMPLEMENTED_BY->my_home;
+}
+
+sub my_desktop {
+ $IMPLEMENTED_BY->can('my_desktop')
+ ? $IMPLEMENTED_BY->my_desktop
+ : Carp::croak("The my_desktop method is not implemented on this platform");
+}
+
+sub my_documents {
+ $IMPLEMENTED_BY->can('my_documents')
+ ? $IMPLEMENTED_BY->my_documents
+ : Carp::croak("The my_documents method is not implemented on this platform");
+}
+
+sub my_music {
+ $IMPLEMENTED_BY->can('my_music')
+ ? $IMPLEMENTED_BY->my_music
+ : Carp::croak("The my_music method is not implemented on this platform");
+}
+
+sub my_pictures {
+ $IMPLEMENTED_BY->can('my_pictures')
+ ? $IMPLEMENTED_BY->my_pictures
+ : Carp::croak("The my_pictures method is not implemented on this platform");
+}
+
+sub my_videos {
+ $IMPLEMENTED_BY->can('my_videos')
+ ? $IMPLEMENTED_BY->my_videos
+ : Carp::croak("The my_videos method is not implemented on this platform");
+}
+
+sub my_data {
+ $IMPLEMENTED_BY->can('my_data')
+ ? $IMPLEMENTED_BY->my_data
+ : Carp::croak("The my_data method is not implemented on this platform");
+}
+
+
+sub my_dist_data {
+ my $params = ref $_[-1] eq 'HASH' ? pop : {};
+ my $dist = pop or Carp::croak("The my_dist_data method requires an argument");
+ my $data = my_data();
+
+ # If datadir is not defined, there's nothing we can do: bail out
+ # and return nothing...
+ return undef unless defined $data;
+
+ # On traditional unixes, hide the top-level directory
+ my $var = $data eq home()
+ ? File::Spec->catdir( $data, '.perl', 'dist', $dist )
+ : File::Spec->catdir( $data, 'Perl', 'dist', $dist );
+
+ # directory exists: return it
+ return $var if -d $var;
+
+ # directory doesn't exist: check if we need to create it...
+ return undef unless $params->{create};
+
+ # user requested directory creation
+ require File::Path;
+ File::Path::mkpath( $var );
+ return $var;
+}
+
+sub my_dist_config {
+ my $params = ref $_[-1] eq 'HASH' ? pop : {};
+ my $dist = pop or Carp::croak("The my_dist_config method requires an argument");
+
+ # not all platforms support a specific my_config() method
+ my $config = $IMPLEMENTED_BY->can('my_config')
+ ? $IMPLEMENTED_BY->my_config
+ : $IMPLEMENTED_BY->my_documents;
+
+ # If neither configdir nor my_documents is defined, there's
+ # nothing we can do: bail out and return nothing...
+ return undef unless defined $config;
+
+ # On traditional unixes, hide the top-level dir
+ my $etc = $config eq home()
+ ? File::Spec->catdir( $config, '.perl', $dist )
+ : File::Spec->catdir( $config, 'Perl', $dist );
+
+ # directory exists: return it
+ return $etc if -d $etc;
+
+ # directory doesn't exist: check if we need to create it...
+ return undef unless $params->{create};
+
+ # user requested directory creation
+ require File::Path;
+ File::Path::mkpath( $etc );
+ return $etc;
+}
+
+
+
+
+#####################################################################
+# General User Methods
+
+sub users_home {
+ $IMPLEMENTED_BY->can('users_home')
+ ? $IMPLEMENTED_BY->users_home( $_[-1] )
+ : Carp::croak("The users_home method is not implemented on this platform");
+}
+
+sub users_desktop {
+ $IMPLEMENTED_BY->can('users_desktop')
+ ? $IMPLEMENTED_BY->users_desktop( $_[-1] )
+ : Carp::croak("The users_desktop method is not implemented on this platform");
+}
+
+sub users_documents {
+ $IMPLEMENTED_BY->can('users_documents')
+ ? $IMPLEMENTED_BY->users_documents( $_[-1] )
+ : Carp::croak("The users_documents method is not implemented on this platform");
+}
+
+sub users_music {
+ $IMPLEMENTED_BY->can('users_music')
+ ? $IMPLEMENTED_BY->users_music( $_[-1] )
+ : Carp::croak("The users_music method is not implemented on this platform");
+}
+
+sub users_pictures {
+ $IMPLEMENTED_BY->can('users_pictures')
+ ? $IMPLEMENTED_BY->users_pictures( $_[-1] )
+ : Carp::croak("The users_pictures method is not implemented on this platform");
+}
+
+sub users_videos {
+ $IMPLEMENTED_BY->can('users_videos')
+ ? $IMPLEMENTED_BY->users_videos( $_[-1] )
+ : Carp::croak("The users_videos method is not implemented on this platform");
+}
+
+sub users_data {
+ $IMPLEMENTED_BY->can('users_data')
+ ? $IMPLEMENTED_BY->users_data( $_[-1] )
+ : Carp::croak("The users_data method is not implemented on this platform");
+}
+
+
+
+
+
+#####################################################################
+# Legacy Methods
+
+# Find the home directory of an arbitrary user
+sub home (;$) {
+ # Allow to be called as a method
+ if ( $_[0] and $_[0] eq 'File::HomeDir' ) {
+ shift();
+ }
+
+ # No params means my home
+ return my_home() unless @_;
+
+ # Check the param
+ my $name = shift;
+ if ( ! defined $name ) {
+ Carp::croak("Can't use undef as a username");
+ }
+ if ( ! length $name ) {
+ Carp::croak("Can't use empty-string (\"\") as a username");
+ }
+
+ # A dot also means my home
+ ### Is this meant to mean File::Spec->curdir?
+ if ( $name eq '.' ) {
+ return my_home();
+ }
+
+ # Now hand off to the implementor
+ $IMPLEMENTED_BY->users_home($name);
+}
+
+
+
+
+
+#####################################################################
+# Tie-Based Interface
+
+# Okay, things below this point get scary
+
+CLASS: {
+ # Make the class for the %~ tied hash:
+ package File::HomeDir::TIE;
+
+ # Make the singleton object.
+ # (We don't use the hash for anything, though)
+ ### THEN WHY MAKE IT???
+ my $SINGLETON = bless {};
+
+ sub TIEHASH { $SINGLETON }
+
+ sub FETCH {
+ # Catch a bad username
+ unless ( defined $_[1] ) {
+ Carp::croak("Can't use undef as a username");
+ }
+
+ # Get our homedir
+ unless ( length $_[1] ) {
+ return File::HomeDir::my_home();
+ }
+
+ # Get a named user's homedir
+ Carp::carp("The tied %~ hash has been deprecated");
+ return File::HomeDir::home($_[1]);
+ }
+
+ sub STORE { _bad('STORE') }
+ sub EXISTS { _bad('EXISTS') }
+ sub DELETE { _bad('DELETE') }
+ sub CLEAR { _bad('CLEAR') }
+ sub FIRSTKEY { _bad('FIRSTKEY') }
+ sub NEXTKEY { _bad('NEXTKEY') }
+
+ sub _bad ($) {
+ Carp::croak("You can't $_[0] with the %~ hash")
+ }
+}
+
+# Do the actual tie of the global %~ variable
+tie %~, 'File::HomeDir::TIE';
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+File::HomeDir - Find your home and other directories on any platform
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Modern Interface (Current User)
+ $home = File::HomeDir->my_home;
+ $desktop = File::HomeDir->my_desktop;
+ $docs = File::HomeDir->my_documents;
+ $music = File::HomeDir->my_music;
+ $pics = File::HomeDir->my_pictures;
+ $videos = File::HomeDir->my_videos;
+ $data = File::HomeDir->my_data;
+ $dist = File::HomeDir->my_dist_data('File-HomeDir');
+ $dist = File::HomeDir->my_dist_config('File-HomeDir');
+
+ # Modern Interface (Other Users)
+ $home = File::HomeDir->users_home('foo');
+ $desktop = File::HomeDir->users_desktop('foo');
+ $docs = File::HomeDir->users_documents('foo');
+ $music = File::HomeDir->users_music('foo');
+ $pics = File::HomeDir->users_pictures('foo');
+ $video = File::HomeDir->users_videos('foo');
+ $data = File::HomeDir->users_data('foo');
+
+=head1 DESCRIPTION
+
+B<File::HomeDir> is a module for locating the directories that are "owned"
+by a user (typicaly your user) and to solve the various issues that arise
+trying to find them consistently across a wide variety of platforms.
+
+The end result is a single API that can find your resources on any platform,
+making it relatively trivial to create Perl software that works elegantly
+and correctly no matter where you run it.
+
+This module provides two main interfaces.
+
+The first is a modern L<File::Spec>-style interface with a consistent
+OO API and different implementation modules to support various
+platforms. You are B<strongly> recommended to use this interface.
+
+The second interface is for legacy support of the original 0.07 interface
+that exported a C<home()> function by default and tied the C<%~> variable.
+
+It is generally not recommended that you use this interface, but due to
+back-compatibility reasons they will remain supported until at least 2010.
+
+The C<%~> interface has been deprecated. Documentation was removed in 2009,
+Unit test were removed in 2011, usage will issue warnings from 2012, and the
+interface will be removed entirely in 2015 (in line with the general Perl
+toolchain convention of a 10 year support period for legacy APIs that
+are potentially or actually in common use).
+
+=head2 Platform Neutrality
+
+In the Unix world, many different types of data can be mixed together
+in your home directory (although on some Unix platforms this is no longer
+the case, particularly for "desktop"-oriented platforms).
+
+On some non-Unix platforms, separate directories are allocated for
+different types of data and have been for a long time.
+
+When writing applications on top of B<File::HomeDir>, you should thus
+always try to use the most specific method you can. User documents should
+be saved in C<my_documents>, data that supports an application but isn't
+normally editing by the user directory should go into C<my_data>.
+
+On platforms that do not make any distinction, all these different
+methods will harmlessly degrade to the main home directory, but on
+platforms that care B<File::HomeDir> will always try to Do The Right
+Thing(tm).
+
+=head1 METHODS
+
+Two types of methods are provided. The C<my_method> series of methods for
+finding resources for the current user, and the C<users_method> (read as
+"user's method") series for finding resources for arbitrary users.
+
+This split is necessary, as on most platforms it is B<much> easier to find
+information about the current user compared to other users, and indeed
+on a number you cannot find out information such as C<users_desktop> at
+all, due to security restrictions.
+
+All methods will double check (using a C<-d> test) that a directory
+actually exists before returning it, so you may trust in the values
+that are returned (subject to the usual caveats of race conditions of
+directories being deleted at the moment between a directory being returned
+and you using it).
+
+However, because in some cases platforms may not support the concept of home
+directories at all, any method may return C<undef> (both in scalar and list
+context) to indicate that there is no matching directory on the system.
+
+For example, most untrusted 'nobody'-type users do not have a home
+directory. So any modules that are used in a CGI application that
+at some level of recursion use your code, will result in calls to
+File::HomeDir returning undef, even for a basic home() call.
+
+=head2 my_home
+
+The C<my_home> method takes no arguments and returns the main home/profile
+directory for the current user.
+
+If the distinction is important to you, the term "current" refers to the
+real user, and not the effective user.
+
+This is also the case for all of the other "my" methods.
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a home directory, or dies on error.
+
+=head2 my_desktop
+
+The C<my_desktop> method takes no arguments and returns the "desktop"
+directory for the current user.
+
+Due to the diversity and complexity of implementions required to deal with
+implementing the required functionality fully and completely, the
+C<my_desktop> method may or may not be implemented on each platform.
+
+That said, I am extremely interested in code to implement C<my_desktop> on
+Unix, as long as it is capable of dealing (as the Windows implementation
+does) with internationalisation. It should also avoid false positive
+results by making sure it only returns the appropriate directories for the
+appropriate platforms.
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a desktop directory, or dies on error.
+
+=head2 my_documents
+
+The C<my_documents> method takes no arguments and returns the directory (for
+the current user) where the user's documents are stored.
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a documents directory, or dies on error.
+
+=head2 my_music
+
+The C<my_music> method takes no arguments and returns the directory
+where the current user's music is stored.
+
+No bias is made to any particular music type or music program, rather the
+concept of a directory to hold the user's music is made at the level of the
+underlying operating system or (at least) desktop environment.
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a suitable directory, or dies on error.
+
+=head2 my_pictures
+
+The C<my_pictures> method takes no arguments and returns the directory
+where the current user's pictures are stored.
+
+No bias is made to any particular picture type or picture program, rather the
+concept of a directory to hold the user's pictures is made at the level of the
+underlying operating system or (at least) desktop environment.
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a suitable directory, or dies on error.
+
+=head2 my_videos
+
+The C<my_videos> method takes no arguments and returns the directory
+where the current user's videos are stored.
+
+No bias is made to any particular video type or video program, rather the
+concept of a directory to hold the user's videos is made at the level of the
+underlying operating system or (at least) desktop environment.
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a suitable directory, or dies on error.
+
+=head2 my_data
+
+The C<my_data> method takes no arguments and returns the directory where
+local applications should stored their internal data for the current
+user.
+
+Generally an application would create a subdirectory such as C<.foo>,
+beneath this directory, and store its data there. By creating your
+directory this way, you get an accurate result on the maximum number of
+platforms. But see the documentation about C<my_dist_config()> or
+C<my_dist_data()> below.
+
+For example, on Unix you get C<~/.foo> and on Win32 you get
+C<~/Local Settings/Application Data/.foo>
+
+Returns the directory path as a string, C<undef> if the current user
+does not have a data directory, or dies on error.
+
+
+=head2 my_dist_config
+
+ File::HomeDir->my_dist_config( $dist [, \%params] );
+
+ # For example...
+
+ File::HomeDir->my_dist_config( 'File-HomeDir' );
+ File::HomeDir->my_dist_config( 'File-HomeDir', { create => 1 } );
+
+The C<my_dist_config> method takes a distribution name as argument and
+returns an application-specific directory where they should store their
+internal configuration.
+
+The base directory will be either C<my_config> if the platform supports
+it, or C<my_documents> otherwise. The subdirectory itself will be
+C<BASE/Perl/Dist-Name>. If the base directory is the user's homedir,
+C<my_dist_config> will be in C<~/.perl/Dist-Name> (and thus be hidden on
+all Unixes).
+
+The optional last argument is a hash reference to tweak the method
+behaviour. The following hash keys are recognized:
+
+=over 4
+
+=item * create
+
+Passing a true value to this key will force the creation of the
+directory if it doesn't exist (remember that C<File::HomeDir>'s policy
+is to return C<undef> if the directory doesn't exist).
+
+Defaults to false, meaning no automatic creation of directory.
+
+=back
+
+
+=head2 my_dist_data
+
+ File::HomeDir->my_dist_data( $dist [, \%params] );
+
+ # For example...
+
+ File::HomeDir->my_dist_data( 'File-HomeDir' );
+ File::HomeDir->my_dist_data( 'File-HomeDir', { create => 1 } );
+
+The C<my_dist_data> method takes a distribution name as argument and
+returns an application-specific directory where they should store their
+internal data.
+
+This directory will be of course a subdirectory of C<my_data>. Platforms
+supporting data-specific directories will use
+C<DATA_DIR/perl/dist/Dist-Name> following the common
+"DATA/vendor/application" pattern. If the C<my_data> directory is the
+user's homedir, C<my_dist_data> will be in C<~/.perl/dist/Dist-Name>
+(and thus be hidden on all Unixes).
+
+The optional last argument is a hash reference to tweak the method
+behaviour. The following hash keys are recognized:
+
+=over 4
+
+=item * create
+
+Passing a true value to this key will force the creation of the
+directory if it doesn't exist (remember that C<File::HomeDir>'s policy
+is to return C<undef> if the directory doesn't exist).
+
+Defaults to false, meaning no automatic creation of directory.
+
+=back
+
+=head2 users_home
+
+ $home = File::HomeDir->users_home('foo');
+
+The C<users_home> method takes a single param and is used to locate the
+parent home/profile directory for an identified user on the system.
+
+While most of the time this identifier would be some form of user name,
+it is permitted to vary per-platform to support user ids or UUIDs as
+applicable for that platform.
+
+Returns the directory path as a string, C<undef> if that user
+does not have a home directory, or dies on error.
+
+=head2 users_documents
+
+ $docs = File::HomeDir->users_documents('foo');
+
+Returns the directory path as a string, C<undef> if that user
+does not have a documents directory, or dies on error.
+
+=head2 users_data
+
+ $data = File::HomeDir->users_data('foo');
+
+Returns the directory path as a string, C<undef> if that user
+does not have a data directory, or dies on error.
+
+=head1 FUNCTIONS
+
+=head2 home
+
+ use File::HomeDir;
+ $home = home();
+ $home = home('foo');
+ $home = File::HomeDir::home();
+ $home = File::HomeDir::home('foo');
+
+The C<home> function is exported by default and is provided for
+compatibility with legacy applications. In new applications, you should
+use the newer method-based interface above.
+
+Returns the directory path to a named user's home/profile directory.
+
+If provided no param, returns the directory path to the current user's
+home/profile directory.
+
+=head1 TO DO
+
+=over 4
+
+=item * Add more granularity to Unix, and add support to VMS and other
+esoteric platforms, so we can consider going core.
+
+=item * Add consistent support for users_* methods
+
+=back
+
+=head1 SUPPORT
+
+This module is stored in an Open Repository at the following address.
+
+L<http://svn.ali.as/cpan/trunk/File-HomeDir>
+
+Write access to the repository is made available automatically to any
+published CPAN author, and to most other volunteers on request.
+
+If you are able to submit your bug report in the form of new (failing)
+unit tests, or can apply your fix directly instead of submitting a patch,
+you are B<strongly> encouraged to do so as the author currently maintains
+over 100 modules and it can take some time to deal with non-Critical bug
+reports or patches.
+
+This will guarantee that your issue will be addressed in the next
+release of the module.
+
+If you cannot provide a direct test or fix, or don't have time to do so,
+then regular bug reports are still accepted and appreciated via the CPAN
+bug tracker.
+
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-HomeDir>
+
+For other issues, for commercial enhancement or support, or to have your
+write access enabled for the repository, contact the author at the email
+address above.
+
+=head1 ACKNOWLEDGEMENTS
+
+The biggest acknowledgement goes to Chris Nandor, who wielded his
+legendary Mac-fu and turned my initial fairly ordinary Darwin
+implementation into something that actually worked properly everywhere,
+and then donated a Mac OS X license to allow it to be maintained properly.
+
+=head1 AUTHORS
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+Sean M. Burke E<lt>sburke@cpan.orgE<gt>
+
+Chris Nandor E<lt>cnandor@cpan.orgE<gt>
+
+Stephen Steneker E<lt>stennie@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<File::ShareDir>, L<File::HomeDir::Win32> (legacy)
+
+=head1 COPYRIGHT
+
+Copyright 2005 - 2012 Adam Kennedy.
+
+Some parts copyright 2000 Sean M. Burke.
+
+Some parts copyright 2006 Chris Nandor.
+
+Some parts copyright 2006 Stephen Steneker.
+
+Some parts copyright 2009-2011 Jérôme Quelin.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/File/HomeDir/Darwin.pm b/lib/File/HomeDir/Darwin.pm
new file mode 100644
index 0000000..7990eb7
--- /dev/null
+++ b/lib/File/HomeDir/Darwin.pm
@@ -0,0 +1,152 @@
+package File::HomeDir::Darwin;
+
+use 5.00503;
+use strict;
+use Cwd ();
+use Carp ();
+use File::HomeDir::Unix ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Unix';
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ my $class = shift;
+
+ if ( exists $ENV{HOME} and defined $ENV{HOME} ) {
+ return $ENV{HOME};
+ }
+
+ my $home = (getpwuid($<))[7];
+ return $home if $home && -d $home;
+
+ return undef;
+}
+
+sub _my_home {
+ my($class, $path) = @_;
+ my $home = $class->my_home;
+ return undef unless defined $home;
+
+ my $folder = "$home/$path";
+ unless ( -d $folder ) {
+ # Make sure that symlinks resolve to directories.
+ return undef unless -l $folder;
+ my $dir = readlink $folder or return;
+ return undef unless -d $dir;
+ }
+
+ return Cwd::abs_path($folder);
+}
+
+sub my_desktop {
+ my $class = shift;
+ $class->_my_home('Desktop');
+}
+
+sub my_documents {
+ my $class = shift;
+ $class->_my_home('Documents');
+}
+
+sub my_data {
+ my $class = shift;
+ $class->_my_home('Library/Application Support');
+}
+
+sub my_music {
+ my $class = shift;
+ $class->_my_home('Music');
+}
+
+sub my_pictures {
+ my $class = shift;
+ $class->_my_home('Pictures');
+}
+
+sub my_videos {
+ my $class = shift;
+ $class->_my_home('Movies');
+}
+
+
+
+
+
+#####################################################################
+# Arbitrary User Methods
+
+sub users_home {
+ my $class = shift;
+ my $home = $class->SUPER::users_home(@_);
+ return defined $home ? Cwd::abs_path($home) : undef;
+}
+
+sub users_desktop {
+ my ($class, $name) = @_;
+ return undef if $name eq 'root';
+ $class->_to_user( $class->my_desktop, $name );
+}
+
+sub users_documents {
+ my ($class, $name) = @_;
+ return undef if $name eq 'root';
+ $class->_to_user( $class->my_documents, $name );
+}
+
+sub users_data {
+ my ($class, $name) = @_;
+ $class->_to_user( $class->my_data, $name )
+ ||
+ $class->users_home($name);
+}
+
+# cheap hack ... not entirely reliable, perhaps, but ... c'est la vie, since
+# there's really no other good way to do it at this time, that i know of -- pudge
+sub _to_user {
+ my ($class, $path, $name) = @_;
+ my $my_home = $class->my_home;
+ my $users_home = $class->users_home($name);
+ defined $users_home or return undef;
+ $path =~ s/^\Q$my_home/$users_home/;
+ return $path;
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Darwin - Find your home and other directories on Darwin (OS X)
+
+=head1 DESCRIPTION
+
+This module provides Mac OS X specific file path for determining
+common user directories in pure perl, by just using C<$ENV{HOME}>
+without Carbon nor Cocoa API calls. In normal usage this module will
+always be used via L<File::HomeDir>.
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user
+ $home = File::HomeDir->my_home; # /Users/mylogin
+ $desktop = File::HomeDir->my_desktop; # /Users/mylogin/Desktop
+ $docs = File::HomeDir->my_documents; # /Users/mylogin/Documents
+ $music = File::HomeDir->my_music; # /Users/mylogin/Music
+ $pics = File::HomeDir->my_pictures; # /Users/mylogin/Pictures
+ $videos = File::HomeDir->my_videos; # /Users/mylogin/Movies
+ $data = File::HomeDir->my_data; # /Users/mylogin/Library/Application Support
+
+=cut
diff --git a/lib/File/HomeDir/Darwin/Carbon.pm b/lib/File/HomeDir/Darwin/Carbon.pm
new file mode 100644
index 0000000..496a1f2
--- /dev/null
+++ b/lib/File/HomeDir/Darwin/Carbon.pm
@@ -0,0 +1,210 @@
+package File::HomeDir::Darwin::Carbon;
+
+# Basic implementation for the Dawin family of operating systems.
+# This includes (most prominently) Mac OS X.
+
+use 5.00503;
+use strict;
+use Cwd ();
+use Carp ();
+use File::HomeDir::Darwin ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+
+ # This is only a child class of the pure Perl darwin
+ # class so that we can do homedir detection of all three
+ # drivers at one via ->isa.
+ @ISA = 'File::HomeDir::Darwin';
+
+ # Load early if in a forking environment and we have
+ # prefork, or at run-time if not.
+ local $@;
+ eval "use prefork 'Mac::Files'";
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ my $class = shift;
+
+ # A lot of unix people and unix-derived tools rely on
+ # the ability to overload HOME. We will support it too
+ # so that they can replace raw HOME calls with File::HomeDir.
+ if ( exists $ENV{HOME} and defined $ENV{HOME} ) {
+ return $ENV{HOME};
+ }
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kCurrentUserFolderType(),
+ );
+}
+
+sub my_desktop {
+ my $class = shift;
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kDesktopFolderType(),
+ );
+}
+
+sub my_documents {
+ my $class = shift;
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kDocumentsFolderType(),
+ );
+}
+
+sub my_data {
+ my $class = shift;
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kApplicationSupportFolderType(),
+ );
+}
+
+sub my_music {
+ my $class = shift;
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kMusicDocumentsFolderType(),
+ );
+}
+
+sub my_pictures {
+ my $class = shift;
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kPictureDocumentsFolderType(),
+ );
+}
+
+sub my_videos {
+ my $class = shift;
+
+ require Mac::Files;
+ $class->_find_folder(
+ Mac::Files::kMovieDocumentsFolderType(),
+ );
+}
+
+sub _find_folder {
+ my $class = shift;
+ my $name = shift;
+
+ require Mac::Files;
+ my $folder = Mac::Files::FindFolder(
+ Mac::Files::kUserDomain(),
+ $name,
+ );
+ return undef unless defined $folder;
+
+ unless ( -d $folder ) {
+ # Make sure that symlinks resolve to directories.
+ return undef unless -l $folder;
+ my $dir = readlink $folder or return;
+ return undef unless -d $dir;
+ }
+
+ return Cwd::abs_path($folder);
+}
+
+
+
+
+
+#####################################################################
+# Arbitrary User Methods
+
+sub users_home {
+ my $class = shift;
+ my $home = $class->SUPER::users_home(@_);
+ return defined $home ? Cwd::abs_path($home) : undef;
+}
+
+# in theory this can be done, but for now, let's cheat, since the
+# rest is Hard
+sub users_desktop {
+ my ($class, $name) = @_;
+ return undef if $name eq 'root';
+ $class->_to_user( $class->my_desktop, $name );
+}
+
+sub users_documents {
+ my ($class, $name) = @_;
+ return undef if $name eq 'root';
+ $class->_to_user( $class->my_documents, $name );
+}
+
+sub users_data {
+ my ($class, $name) = @_;
+ $class->_to_user( $class->my_data, $name )
+ ||
+ $class->users_home($name);
+}
+
+# cheap hack ... not entirely reliable, perhaps, but ... c'est la vie, since
+# there's really no other good way to do it at this time, that i know of -- pudge
+sub _to_user {
+ my ($class, $path, $name) = @_;
+ my $my_home = $class->my_home;
+ my $users_home = $class->users_home($name);
+ defined $users_home or return undef;
+ $path =~ s/^\Q$my_home/$users_home/;
+ return $path;
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Darwin - Find your home and other directories on Darwin (OS X)
+
+=head1 DESCRIPTION
+
+This module provides Darwin-specific implementations for determining
+common user directories. In normal usage this module will always be
+used via L<File::HomeDir>.
+
+Note -- since this module requires Mac::Carbon and Mac::Carbon does
+not work with 64-bit perls, on such systems, File::HomeDir will try
+L<File::HomeDir::Darwin::Cocoa> and then fall back to the (pure Perl)
+L<File::HomeDir::Darwin>.
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user
+ $home = File::HomeDir->my_home; # /Users/mylogin
+ $desktop = File::HomeDir->my_desktop; # /Users/mylogin/Desktop
+ $docs = File::HomeDir->my_documents; # /Users/mylogin/Documents
+ $music = File::HomeDir->my_music; # /Users/mylogin/Music
+ $pics = File::HomeDir->my_pictures; # /Users/mylogin/Pictures
+ $videos = File::HomeDir->my_videos; # /Users/mylogin/Movies
+ $data = File::HomeDir->my_data; # /Users/mylogin/Library/Application Support
+
+=head1 TODO
+
+=over 4
+
+=item * Test with Mac OS (versions 7, 8, 9)
+
+=item * Some better way for users_* ?
+
+=back
diff --git a/lib/File/HomeDir/Darwin/Cocoa.pm b/lib/File/HomeDir/Darwin/Cocoa.pm
new file mode 100644
index 0000000..b54ea69
--- /dev/null
+++ b/lib/File/HomeDir/Darwin/Cocoa.pm
@@ -0,0 +1,165 @@
+package File::HomeDir::Darwin::Cocoa;
+
+use 5.00503;
+use strict;
+use Cwd ();
+use Carp ();
+use File::HomeDir::Darwin ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Darwin';
+
+ # Load early if in a forking environment and we have
+ # prefork, or at run-time if not.
+ local $@;
+ eval "use prefork 'Mac::SystemDirectory'";
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ my $class = shift;
+
+ # A lot of unix people and unix-derived tools rely on
+ # the ability to overload HOME. We will support it too
+ # so that they can replace raw HOME calls with File::HomeDir.
+ if ( exists $ENV{HOME} and defined $ENV{HOME} ) {
+ return $ENV{HOME};
+ }
+
+ require Mac::SystemDirectory;
+ return Mac::SystemDirectory::HomeDirectory();
+}
+
+# from 10.4
+sub my_desktop {
+ my $class = shift;
+
+ require Mac::SystemDirectory;
+ eval {
+ $class->_find_folder(Mac::SystemDirectory::NSDesktopDirectory())
+ }
+ ||
+ $class->SUPER::my_desktop;
+}
+
+# from 10.2
+sub my_documents {
+ my $class = shift;
+
+ require Mac::SystemDirectory;
+ eval {
+ $class->_find_folder(Mac::SystemDirectory::NSDocumentDirectory())
+ }
+ ||
+ $class->SUPER::my_documents;
+}
+
+# from 10.4
+sub my_data {
+ my $class = shift;
+
+ require Mac::SystemDirectory;
+ eval {
+ $class->_find_folder(Mac::SystemDirectory::NSApplicationSupportDirectory())
+ }
+ ||
+ $class->SUPER::my_data;
+}
+
+# from 10.6
+sub my_music {
+ my $class = shift;
+
+ require Mac::SystemDirectory;
+ eval {
+ $class->_find_folder(Mac::SystemDirectory::NSMusicDirectory())
+ }
+ ||
+ $class->SUPER::my_music;
+}
+
+# from 10.6
+sub my_pictures {
+ my $class = shift;
+
+ require Mac::SystemDirectory;
+ eval {
+ $class->_find_folder(Mac::SystemDirectory::NSPicturesDirectory())
+ }
+ ||
+ $class->SUPER::my_pictures;
+}
+
+# from 10.6
+sub my_videos {
+ my $class = shift;
+
+ require Mac::SystemDirectory;
+ eval {
+ $class->_find_folder(Mac::SystemDirectory::NSMoviesDirectory())
+ }
+ ||
+ $class->SUPER::my_videos;
+}
+
+sub _find_folder {
+ my $class = shift;
+ my $name = shift;
+
+ require Mac::SystemDirectory;
+ my $folder = Mac::SystemDirectory::FindDirectory($name);
+ return undef unless defined $folder;
+
+ unless ( -d $folder ) {
+ # Make sure that symlinks resolve to directories.
+ return undef unless -l $folder;
+ my $dir = readlink $folder or return;
+ return undef unless -d $dir;
+ }
+
+ return Cwd::abs_path($folder);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Darwin::Cocoa - Find your home and other directories on Darwin (OS X)
+
+=head1 DESCRIPTION
+
+This module provides Darwin-specific implementations for determining
+common user directories using Cocoa API through
+L<Mac::SystemDirectory>. In normal usage this module will always be
+used via L<File::HomeDir>.
+
+Theoretically, this should return the same paths as both of the other
+Darwin drivers.
+
+Because this module requires L<Mac::SystemDirectory>, if the module
+is not installed, L<File::HomeDir> will fall back to L<File::HomeDir::Darwin>.
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user
+ $home = File::HomeDir->my_home; # /Users/mylogin
+ $desktop = File::HomeDir->my_desktop; # /Users/mylogin/Desktop
+ $docs = File::HomeDir->my_documents; # /Users/mylogin/Documents
+ $music = File::HomeDir->my_music; # /Users/mylogin/Music
+ $pics = File::HomeDir->my_pictures; # /Users/mylogin/Pictures
+ $videos = File::HomeDir->my_videos; # /Users/mylogin/Movies
+ $data = File::HomeDir->my_data; # /Users/mylogin/Library/Application Support
+
+=cut
diff --git a/lib/File/HomeDir/Driver.pm b/lib/File/HomeDir/Driver.pm
new file mode 100644
index 0000000..348f97b
--- /dev/null
+++ b/lib/File/HomeDir/Driver.pm
@@ -0,0 +1,54 @@
+package File::HomeDir::Driver;
+
+# Abstract base class that provides no functionality,
+# but confirms the class is a File::HomeDir driver class.
+
+use 5.00503;
+use strict;
+use Carp ();
+
+use vars qw{$VERSION};
+BEGIN {
+ $VERSION = '1.00';
+}
+
+sub my_home {
+ Carp::croak("$_[0] does not implement compulsory method $_[1]");
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Driver - Base class for all File::HomeDir drivers
+
+=head1 DESCRIPTION
+
+This module is the base class for all L<File::HomeDir> drivers, and must
+be inherited from to identify a class as a driver.
+
+It is primarily provided as a convenience for this specific identification
+purpose, as L<File::HomeDir> supports the specification of custom drivers
+and an C<-E<gt>isa> check is used during the loading of the driver.
+
+=head1 AUTHOR
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<File::HomeDir>
+
+=head1 COPYRIGHT
+
+Copyright 2009 - 2011 Adam Kennedy.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/File/HomeDir/FreeDesktop.pm b/lib/File/HomeDir/FreeDesktop.pm
new file mode 100644
index 0000000..c006921
--- /dev/null
+++ b/lib/File/HomeDir/FreeDesktop.pm
@@ -0,0 +1,136 @@
+package File::HomeDir::FreeDesktop;
+
+# Specific functionality for unixes running free desktops
+# compatible with (but not using) File-BaseDir-0.03
+
+# See POD at the end of the file for more documentation.
+
+use 5.00503;
+use strict;
+use Carp ();
+use File::Spec ();
+use File::Which ();
+use File::HomeDir::Unix ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Unix';
+}
+
+# xdg uses $ENV{XDG_CONFIG_HOME}/user-dirs.dirs to know where are the
+# various "my xxx" directories. That is a shell file. The official API
+# is the xdg-user-dir executable. It has no provision for assessing
+# the directories of a user that is different than the one we are
+# running under; the standard substitute user mechanisms are needed to
+# overcome this.
+
+my $xdgprog = File::Which::which('xdg-user-dir');
+
+sub _my {
+ # No quoting because input is hard-coded and only comes from this module
+ my $thingy = qx($xdgprog $_[1]);
+ chomp $thingy;
+ return $thingy;
+}
+
+# Simple stuff
+sub my_desktop { shift->_my('DESKTOP') }
+sub my_documents { shift->_my('DOCUMENTS') }
+sub my_music { shift->_my('MUSIC') }
+sub my_pictures { shift->_my('PICTURES') }
+sub my_videos { shift->_my('VIDEOS') }
+
+sub my_data {
+ $ENV{XDG_DATA_HOME}
+ or
+ File::Spec->catdir(
+ shift->my_home,
+ qw{ .local share }
+ );
+}
+
+sub my_config {
+ $ENV{XDG_CONFIG_HOME}
+ or
+ File::Spec->catdir(
+ shift->my_home,
+ qw{ .config }
+ );
+}
+
+# Custom locations (currently undocumented)
+sub my_download { shift->_my('DOWNLOAD') }
+sub my_publicshare { shift->_my('PUBLICSHARE') }
+sub my_templates { shift->_my('TEMPLATES') }
+
+sub my_cache {
+ $ENV{XDG_CACHE_HOME}
+ ||
+ File::Spec->catdir(shift->my_home, qw{ .cache });
+}
+
+
+
+
+
+#####################################################################
+# General User Methods
+
+sub users_desktop { Carp::croak('The users_desktop method is not available on an XDG based system.'); }
+sub users_documents { Carp::croak('The users_documents method is not available on an XDG based system.'); }
+sub users_music { Carp::croak('The users_music method is not available on an XDG based system.'); }
+sub users_pictures { Carp::croak('The users_pictures method is not available on an XDG based system.'); }
+sub users_videos { Carp::croak('The users_videos method is not available on an XDG based system.'); }
+sub users_data { Carp::croak('The users_data method is not available on an XDG based system.'); }
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::FreeDesktop - Find your home and other directories on FreeDesktop.org Unix
+
+=head1 DESCRIPTION
+
+This module provides implementations for determining common user
+directories. In normal usage this module will always be
+used via L<File::HomeDir>.
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user
+ $home = File::HomeDir->my_home; # /home/mylogin
+ $desktop = File::HomeDir->my_desktop;
+ $docs = File::HomeDir->my_documents;
+ $music = File::HomeDir->my_music;
+ $pics = File::HomeDir->my_pictures;
+ $videos = File::HomeDir->my_videos;
+ $data = File::HomeDir->my_data;
+
+=head1 AUTHORS
+
+Jerome Quelin E<lt>jquellin@cpan.org<gt>
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<File::HomeDir>, L<File::HomeDir::Win32> (legacy)
+
+=head1 COPYRIGHT
+
+Copyright 2009 - 2011 Jerome Quelin.
+
+Some parts copyright 2010 Adam Kennedy.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/File/HomeDir/MacOS9.pm b/lib/File/HomeDir/MacOS9.pm
new file mode 100644
index 0000000..c88ec34
--- /dev/null
+++ b/lib/File/HomeDir/MacOS9.pm
@@ -0,0 +1,150 @@
+package File::HomeDir::MacOS9;
+
+# Half-assed implementation for the legacy Mac OS9 operating system.
+# Provided mainly to provide legacy compatibility. May be removed at
+# a later date.
+
+use 5.00503;
+use strict;
+use Carp ();
+use File::HomeDir::Driver ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Driver';
+}
+
+# Load early if in a forking environment and we have
+# prefork, or at run-time if not.
+SCOPE: {
+ local $@;
+ eval "use prefork 'Mac::Files'";
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ my $class = shift;
+
+ # Try for $ENV{HOME} if we have it
+ if ( defined $ENV{HOME} ) {
+ return $ENV{HOME};
+ }
+
+ ### DESPERATION SETS IN
+
+ # We could use the desktop
+ SCOPE: {
+ local $@;
+ eval {
+ my $home = $class->my_desktop;
+ return $home if $home and -d $home;
+ };
+ }
+
+ # Desperation on any platform
+ SCOPE: {
+ # On some platforms getpwuid dies if called at all
+ local $SIG{'__DIE__'} = '';
+ my $home = (getpwuid($<))[7];
+ return $home if $home and -d $home;
+ }
+
+ Carp::croak("Could not locate current user's home directory");
+}
+
+sub my_desktop {
+ my $class = shift;
+
+ # Find the desktop via Mac::Files
+ local $SIG{'__DIE__'} = '';
+ require Mac::Files;
+ my $home = Mac::Files::FindFolder(
+ Mac::Files::kOnSystemDisk(),
+ Mac::Files::kDesktopFolderType(),
+ );
+ return $home if $home and -d $home;
+
+ Carp::croak("Could not locate current user's desktop");
+}
+
+
+
+
+
+#####################################################################
+# General User Methods
+
+sub users_home {
+ my ($class, $name) = @_;
+
+ SCOPE: {
+ # On some platforms getpwnam dies if called at all
+ local $SIG{'__DIE__'} = '';
+ my $home = (getpwnam($name))[7];
+ return $home if defined $home and -d $home;
+ }
+
+ Carp::croak("Failed to find home directory for user '$name'");
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::MacOS9 - Find your home and other directories on legacy Macs
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user
+ $home = File::HomeDir->my_home;
+ $desktop = File::HomeDir->my_desktop;
+
+=head1 DESCRIPTION
+
+This module provides implementations for determining common user
+directories on legacy Mac hosts. In normal usage this module will always be
+used via L<File::HomeDir>.
+
+This module is no longer actively maintained, and is included only for
+extreme back-compatibility.
+
+Only the C<my_home> and C<my_desktop> methods are supported.
+
+=head1 SUPPORT
+
+See the support section the main L<File::HomeDir> module.
+
+=head1 AUTHORS
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+Sean M. Burke E<lt>sburke@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<File::HomeDir>
+
+=head1 COPYRIGHT
+
+Copyright 2005 - 2011 Adam Kennedy.
+
+Some parts copyright 2000 Sean M. Burke.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/File/HomeDir/Test.pm b/lib/File/HomeDir/Test.pm
new file mode 100644
index 0000000..8d0e12c
--- /dev/null
+++ b/lib/File/HomeDir/Test.pm
@@ -0,0 +1,137 @@
+package File::HomeDir::Test;
+
+use 5.00503;
+use strict;
+use Carp ();
+use File::Spec ();
+use File::Temp ();
+use File::HomeDir::Driver ();
+
+use vars qw{$VERSION @ISA %DIR $ENABLED};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Driver';
+ %DIR = ();
+ $ENABLED = 0;
+}
+
+# Special magic use in test scripts
+sub import {
+ my $class = shift;
+ die "Attempted to initialise File::HomeDir::Test trice" if %DIR;
+
+ # Fill the test directories
+ my $BASE = File::Temp::tempdir( CLEANUP => 1 );
+ %DIR = map { $_ => File::Spec->catdir( $BASE, $_ ) } qw{
+ my_home
+ my_desktop
+ my_documents
+ my_data
+ my_music
+ my_pictures
+ my_videos
+ };
+
+ # Hijack HOME to the home directory
+ $ENV{HOME} = $DIR{my_home};
+
+ # Make File::HomeDir load us instead of the native driver
+ $File::HomeDir::IMPLEMENTED_BY = # Prevent a warning
+ $File::HomeDir::IMPLEMENTED_BY = 'File::HomeDir::Test';
+
+ # Ready to go
+ $ENABLED = 1;
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ mkdir($DIR{my_home}, 0755) unless -d $DIR{my_home};
+ return $DIR{my_home};
+}
+
+sub my_desktop {
+ mkdir($DIR{my_desktop}, 0755) unless -d $DIR{my_desktop};
+ return $DIR{my_desktop};
+}
+
+sub my_documents {
+ mkdir($DIR{my_documents}, 0755) unless -f $DIR{my_documents};
+ return $DIR{my_documents};
+}
+
+sub my_data {
+ mkdir($DIR{my_data}, 0755) unless -d $DIR{my_data};
+ return $DIR{my_data};
+}
+
+sub my_music {
+ mkdir($DIR{my_music}, 0755) unless -d $DIR{my_music};
+ return $DIR{my_music};
+}
+
+sub my_pictures {
+ mkdir($DIR{my_pictures}, 0755) unless -d $DIR{my_pictures};
+ return $DIR{my_pictures};
+}
+
+sub my_videos {
+ mkdir($DIR{my_videos}, 0755) unless -d $DIR{my_videos};
+ return $DIR{my_videos};
+}
+
+sub users_home {
+ return undef;
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Test - Prevent the accidental creation of user-owned files during testing
+
+=head1 SYNOPSIS
+
+ use Test::More test => 1;
+ use File::HomeDir::Test;
+ use File::HomeDir;
+
+=head1 DESCRIPTION
+
+B<File::HomeDir::Test> is a L<File::HomeDir> driver intended for use in the test scripts
+of modules or applications that write files into user-owned directories.
+
+It is designed to prevent the pollution of user directories with files that are not part
+of the application install itself, but were created during testing. These files can leak
+state information from the tests into the run-time usage of an application, and on Unix
+systems also prevents tests (which may be executed as root via sudo) from writing files
+which cannot later be modified or removed by the regular user.
+
+=head1 SUPPORT
+
+See the support section of the main L<File::HomeDir> documentation.
+
+=head1 AUTHOR
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2005 - 2011 Adam Kennedy.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/File/HomeDir/Unix.pm b/lib/File/HomeDir/Unix.pm
new file mode 100644
index 0000000..6e3c3a1
--- /dev/null
+++ b/lib/File/HomeDir/Unix.pm
@@ -0,0 +1,186 @@
+package File::HomeDir::Unix;
+
+# See POD at the end of the file for documentation
+
+use 5.00503;
+use strict;
+use Carp ();
+use File::HomeDir::Driver ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Driver';
+}
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ my $class = shift;
+ my $home = $class->_my_home(@_);
+
+ # On Unix in general, a non-existant home means "no home"
+ # For example, "nobody"-like users might use /nonexistant
+ if ( defined $home and ! -d $home ) {
+ $home = undef;
+ }
+
+ return $home;
+}
+
+sub _my_home {
+ my $class = shift;
+ if ( exists $ENV{HOME} and defined $ENV{HOME} ) {
+ return $ENV{HOME};
+ }
+
+ # This is from the original code, but I'm guessing
+ # it means "login directory" and exists on some Unixes.
+ if ( exists $ENV{LOGDIR} and $ENV{LOGDIR} ) {
+ return $ENV{LOGDIR};
+ }
+
+ ### More-desperate methods
+
+ # Light desperation on any (Unixish) platform
+ SCOPE: {
+ my $home = (getpwuid($<))[7];
+ return $home if $home and -d $home;
+ }
+
+ return undef;
+}
+
+# On unix by default, everything is under the same folder
+sub my_desktop {
+ shift->my_home;
+}
+
+sub my_documents {
+ shift->my_home;
+}
+
+sub my_data {
+ shift->my_home;
+}
+
+sub my_music {
+ shift->my_home;
+}
+
+sub my_pictures {
+ shift->my_home;
+}
+
+sub my_videos {
+ shift->my_home;
+}
+
+
+
+
+
+#####################################################################
+# General User Methods
+
+sub users_home {
+ my ($class, $name) = @_;
+
+ # IF and only if we have getpwuid support, and the
+ # name of the user is our own, shortcut to my_home.
+ # This is needed to handle HOME environment settings.
+ if ( $name eq getpwuid($<) ) {
+ return $class->my_home;
+ }
+
+ SCOPE: {
+ my $home = (getpwnam($name))[7];
+ return $home if $home and -d $home;
+ }
+
+ return undef;
+}
+
+sub users_desktop {
+ shift->users_home(@_);
+}
+
+sub users_documents {
+ shift->users_home(@_);
+}
+
+sub users_data {
+ shift->users_home(@_);
+}
+
+sub users_music {
+ shift->users_home(@_);
+}
+
+sub users_pictures {
+ shift->users_home(@_);
+}
+
+sub users_videos {
+ shift->users_home(@_);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Unix - Find your home and other directories on legacy Unix
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user
+ $home = File::HomeDir->my_home; # /home/mylogin
+ $desktop = File::HomeDir->my_desktop; # All of these will...
+ $docs = File::HomeDir->my_documents; # ...default to home...
+ $music = File::HomeDir->my_music; # ...directory
+ $pics = File::HomeDir->my_pictures; #
+ $videos = File::HomeDir->my_videos; #
+ $data = File::HomeDir->my_data; #
+
+=head1 DESCRIPTION
+
+This module provides implementations for determining common user
+directories. In normal usage this module will always be
+used via L<File::HomeDir>.
+
+=head1 SUPPORT
+
+See the support section the main L<File::HomeDir> module.
+
+=head1 AUTHORS
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+Sean M. Burke E<lt>sburke@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<File::HomeDir>, L<File::HomeDir::Win32> (legacy)
+
+=head1 COPYRIGHT
+
+Copyright 2005 - 2011 Adam Kennedy.
+
+Some parts copyright 2000 Sean M. Burke.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/File/HomeDir/Windows.pm b/lib/File/HomeDir/Windows.pm
new file mode 100644
index 0000000..4e1de6a
--- /dev/null
+++ b/lib/File/HomeDir/Windows.pm
@@ -0,0 +1,241 @@
+package File::HomeDir::Windows;
+
+# See POD at the end of the file for documentation
+
+use 5.00503;
+use strict;
+use Carp ();
+use File::Spec ();
+use File::HomeDir::Driver ();
+
+use vars qw{$VERSION @ISA};
+BEGIN {
+ $VERSION = '1.00';
+ @ISA = 'File::HomeDir::Driver';
+}
+
+sub CREATE () { 1 }
+
+
+
+
+
+#####################################################################
+# Current User Methods
+
+sub my_home {
+ my $class = shift;
+
+ # A lot of unix people and unix-derived tools rely on
+ # the ability to overload HOME. We will support it too
+ # so that they can replace raw HOME calls with File::HomeDir.
+ if ( exists $ENV{HOME} and $ENV{HOME} ) {
+ return $ENV{HOME};
+ }
+
+ # Do we have a user profile?
+ if ( exists $ENV{USERPROFILE} and $ENV{USERPROFILE} ) {
+ return $ENV{USERPROFILE};
+ }
+
+ # Some Windows use something like $ENV{HOME}
+ if ( exists $ENV{HOMEDRIVE} and exists $ENV{HOMEPATH} and $ENV{HOMEDRIVE} and $ENV{HOMEPATH} ) {
+ return File::Spec->catpath(
+ $ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '',
+ );
+ }
+
+ return undef;
+}
+
+sub my_desktop {
+ my $class = shift;
+
+ # The most correct way to find the desktop
+ SCOPE: {
+ require Win32;
+ my $dir = Win32::GetFolderPath(Win32::CSIDL_DESKTOP(), CREATE);
+ return $dir if $dir and $class->_d($dir);
+ }
+
+ # MSWindows sets WINDIR, MS WinNT sets USERPROFILE.
+ foreach my $e ( 'USERPROFILE', 'WINDIR' ) {
+ next unless $ENV{$e};
+ my $desktop = File::Spec->catdir($ENV{$e}, 'Desktop');
+ return $desktop if $desktop and $class->_d($desktop);
+ }
+
+ # As a last resort, try some hard-wired values
+ foreach my $fixed (
+ # The reason there are both types of slash here is because
+ # this set of paths has been kept from thethe original version
+ # of File::HomeDir::Win32 (before it was rewritten).
+ # I can only assume this is Cygwin-related stuff.
+ "C:\\windows\\desktop",
+ "C:\\win95\\desktop",
+ "C:/win95/desktop",
+ "C:/windows/desktop",
+ ) {
+ return $fixed if $class->_d($fixed);
+ }
+
+ return undef;
+}
+
+sub my_documents {
+ my $class = shift;
+
+ # The most correct way to find my documents
+ SCOPE: {
+ require Win32;
+ my $dir = Win32::GetFolderPath(Win32::CSIDL_PERSONAL(), CREATE);
+ return $dir if $dir and $class->_d($dir);
+ }
+
+ return undef;
+}
+
+sub my_data {
+ my $class = shift;
+
+ # The most correct way to find my documents
+ SCOPE: {
+ require Win32;
+ my $dir = Win32::GetFolderPath(Win32::CSIDL_LOCAL_APPDATA(), CREATE);
+ return $dir if $dir and $class->_d($dir);
+ }
+
+ return undef;
+}
+
+sub my_music {
+ my $class = shift;
+
+ # The most correct way to find my music
+ SCOPE: {
+ require Win32;
+ my $dir = Win32::GetFolderPath(Win32::CSIDL_MYMUSIC(), CREATE);
+ return $dir if $dir and $class->_d($dir);
+ }
+
+ return undef;
+}
+
+sub my_pictures {
+ my $class = shift;
+
+ # The most correct way to find my pictures
+ SCOPE: {
+ require Win32;
+ my $dir = Win32::GetFolderPath(Win32::CSIDL_MYPICTURES(), CREATE);
+ return $dir if $dir and $class->_d($dir);
+ }
+
+ return undef;
+}
+
+sub my_videos {
+ my $class = shift;
+
+ # The most correct way to find my videos
+ SCOPE: {
+ require Win32;
+ my $dir = Win32::GetFolderPath(Win32::CSIDL_MYVIDEO(), CREATE);
+ return $dir if $dir and $class->_d($dir);
+ }
+
+ return undef;
+}
+
+# Special case version of -d
+sub _d {
+ my $self = shift;
+ my $path = shift;
+
+ # Window can legally return a UNC path from GetFolderPath.
+ # Not only is the meaning of -d complicated in this situation,
+ # but even on a local network calling -d "\\\\cifs\\path" can
+ # take several seconds. UNC can also do even weirder things,
+ # like launching processes and such.
+ # To avoid various crazy bugs caused by this, we do NOT attempt
+ # to validate UNC paths at all so that the code that is calling
+ # us has an opportunity to take special actions without our
+ # blundering getting in the way.
+ if ( $path =~ /\\\\/ ) {
+ return 1;
+ }
+
+ # Otherwise do a stat as normal
+ return -d $path;
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+File::HomeDir::Windows - Find your home and other directories on Windows
+
+=head1 SYNOPSIS
+
+ use File::HomeDir;
+
+ # Find directories for the current user (eg. using Windows XP Professional)
+ $home = File::HomeDir->my_home; # C:\Documents and Settings\mylogin
+ $desktop = File::HomeDir->my_desktop; # C:\Documents and Settings\mylogin\Desktop
+ $docs = File::HomeDir->my_documents; # C:\Documents and Settings\mylogin\My Documents
+ $music = File::HomeDir->my_music; # C:\Documents and Settings\mylogin\My Documents\My Music
+ $pics = File::HomeDir->my_pictures; # C:\Documents and Settings\mylogin\My Documents\My Pictures
+ $videos = File::HomeDir->my_videos; # C:\Documents and Settings\mylogin\My Documents\My Video
+ $data = File::HomeDir->my_data; # C:\Documents and Settings\mylogin\Local Settings\Application Data
+
+=head1 DESCRIPTION
+
+This module provides Windows-specific implementations for determining
+common user directories. In normal usage this module will always be
+used via L<File::HomeDir>.
+
+Internally this module will use L<Win32>::GetFolderPath to fetch the location
+of your directories. As a result of this, in certain unusual situations
+(usually found inside large organisations) the methods may return UNC paths
+such as C<\\cifs.local\home$>.
+
+If your application runs on Windows and you want to have it work comprehensively
+everywhere, you may need to implement your own handling for these paths as they
+can cause strange behaviour.
+
+For example, stat calls to UNC paths may work but block for several seconds, but
+opendir() may not be able to read any files (creating the appearance of an existing
+but empty directory).
+
+To avoid complicating the problem any further, in the rare situation that a UNC path
+is returned by C<GetFolderPath> the usual -d validation checks will B<not> be done.
+
+=head1 SUPPORT
+
+See the support section the main L<File::HomeDir> module.
+
+=head1 AUTHORS
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+Sean M. Burke E<lt>sburke@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<File::HomeDir>, L<File::HomeDir::Win32> (legacy)
+
+=head1 COPYRIGHT
+
+Copyright 2005 - 2011 Adam Kennedy.
+
+Some parts copyright 2000 Sean M. Burke.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/t/01_compile.t b/t/01_compile.t
new file mode 100644
index 0000000..44b7335
--- /dev/null
+++ b/t/01_compile.t
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+
+# Compile-testing for File::HomeDir
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+use File::Spec::Functions ':ALL';
+
+use Test::More tests => 11;
+
+# This module is destined for the core.
+# Please do NOT use convenience modules
+# use English; <-- don't do this
+
+ok( $] > 5.00503, 'Perl version is 5.005 or newer' );
+
+use_ok( 'File::HomeDir::Driver' );
+use_ok( 'File::HomeDir::Unix' );
+use_ok( 'File::HomeDir::FreeDesktop' );
+use_ok( 'File::HomeDir::Darwin' );
+use_ok( 'File::HomeDir::Darwin::Carbon' );
+use_ok( 'File::HomeDir::Darwin::Cocoa' );
+use_ok( 'File::HomeDir::Windows' );
+use_ok( 'File::HomeDir::MacOS9' );
+use_ok( 'File::HomeDir' );
+
+ok( defined &home, 'Using File::HomeDir exports home()' );
+
+# Note the driver we are using for the purposes of
+# understanding CPAN Testers failure reports.
+diag( "Implemented by: $File::HomeDir::IMPLEMENTED_BY" );
+
+# Prevent a warning
+$File::HomeDir::IMPLEMENTED_BY = $File::HomeDir::IMPLEMENTED_BY;
diff --git a/t/02_main.t b/t/02_main.t
new file mode 100644
index 0000000..7a171ce
--- /dev/null
+++ b/t/02_main.t
@@ -0,0 +1,288 @@
+#!/usr/bin/perl
+
+# Main testing for File::HomeDir
+
+# Testing "home directory" concepts is blood difficult, be delicate in
+# your changes and don't forget to test on every OS at multiple versions
+# (WinXP vs Win2003 etc) as both root and non-root users.
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+use File::Spec::Functions ':ALL';
+use Test::More;
+use File::HomeDir;
+
+# This module is destined for the core.
+# Please do NOT use convenience modules
+# use English; <-- don't do this
+
+sub is_dir($) {
+ my $dir = shift or return;
+ return 1 if -d $dir;
+ return unless -l $dir;
+ $dir = readlink $dir or return;
+ return -d $dir;
+}
+
+
+
+
+
+#####################################################################
+# Environment Detection and Plan
+
+# For what scenarios can we be sure that we have desktop/documents
+my $NO_GETPWUID = 0;
+my $HAVEHOME = 0;
+my $HAVEDESKTOP = 0;
+my $HAVEMUSIC = 0;
+my $HAVEPICTURES = 0;
+my $HAVEVIDEOS = 0;
+my $HAVEDOCUMENTS = 0;
+my $HAVEOTHERS = 0;
+
+# Various cases of things we should try to test for
+# Top level is entire classes of operating system.
+# Below that are more general things.
+if ( $^O eq 'MSWin32' ) {
+ $NO_GETPWUID = 1;
+ $HAVEHOME = 1;
+ $HAVEDESKTOP = 1;
+ $HAVEPICTURES = 1;
+ $HAVEDOCUMENTS = 1;
+ $HAVEOTHERS = 1;
+
+ # My Music does not exist on Win2000
+ require Win32;
+ my @version = Win32::GetOSVersion();
+ my $v = ($version[4]||0)
+ + ($version[1]||0) * 0.001
+ + ($version[2]||0) * 0.000001;
+ if ( $v <= 2.005000 ) {
+ $HAVEMUSIC = 0;
+ $HAVEVIDEOS = 0;
+ } else {
+ $HAVEMUSIC = 1;
+ $HAVEVIDEOS = 0; # If we ever support "maybe" this is a maybe
+ }
+
+# System is unix-like
+
+# Nobody users on all unix systems generally don't have home directories
+} elsif ( getpwuid($<) eq 'nobody' ) {
+ $HAVEHOME = 0;
+ $HAVEDESKTOP = 0;
+ $HAVEMUSIC = 0;
+ $HAVEPICTURES = 0;
+ $HAVEVIDEOS = 0;
+ $HAVEOTHERS = 0;
+
+} elsif (
+ $^O eq 'darwin'
+) {
+ # "Unixes with proper desktops" special cases
+ if ( $ENV{AUTOMATED_TESTING} ) {
+ # Automated testers on Mac (notably BINGOS) will often have
+ # super stripped down testing users.
+ $HAVEHOME = 1;
+ $HAVEDESKTOP = 1;
+ $HAVEMUSIC = 0;
+ $HAVEPICTURES = 0;
+ $HAVEVIDEOS = 0;
+ $HAVEDOCUMENTS = 0;
+ $HAVEOTHERS = 1;
+ } elsif ( $< ) {
+ # Normal user
+ $HAVEHOME = 1;
+ $HAVEDESKTOP = 1;
+ $HAVEMUSIC = 1;
+ $HAVEPICTURES = 1;
+ $HAVEVIDEOS = 1;
+ $HAVEDOCUMENTS = 1;
+ $HAVEOTHERS = 1;
+ } else {
+ # Root can only be relied on to have a home
+ $HAVEHOME = 1;
+ $HAVEDESKTOP = 0;
+ $HAVEMUSIC = 0;
+ $HAVEPICTURES = 0;
+ $HAVEVIDEOS = 0;
+ $HAVEDOCUMENTS = 0;
+ $HAVEOTHERS = 0;
+ }
+
+} elsif ( $File::HomeDir::IMPLEMENTED_BY eq 'File::HomeDir::FreeDesktop' ) {
+ # On FreeDesktop we can't trust people to have a desktop (annoyingly)
+ $HAVEHOME = 1;
+ $HAVEDESKTOP = 0;
+ $HAVEMUSIC = 0;
+ $HAVEVIDEOS = 0;
+ $HAVEPICTURES = 0;
+ $HAVEDOCUMENTS = 0;
+ $HAVEOTHERS = 0;
+
+} else {
+ # Default to traditional Unix
+ $HAVEHOME = 1;
+ $HAVEDESKTOP = 1;
+ $HAVEMUSIC = 1;
+ $HAVEPICTURES = 1;
+ $HAVEVIDEOS = 1;
+ $HAVEDOCUMENTS = 1;
+ $HAVEOTHERS = 1;
+}
+
+plan tests => 39;
+
+
+
+
+
+#####################################################################
+# Test invalid uses
+
+eval {
+ home(undef);
+};
+like( $@, qr{Can\'t use undef as a username}, 'home(undef)' );
+
+
+
+
+
+#####################################################################
+# API Test
+
+# Check the methods all exist
+foreach ( qw{ home desktop documents music pictures videos data } ) {
+ can_ok( 'File::HomeDir', "my_$_" );
+ can_ok( 'File::HomeDir', "users_$_" );
+}
+
+
+
+
+
+#####################################################################
+# Main Tests
+
+# Find this user's homedir
+my $home = home();
+if ( $HAVEHOME ) {
+ ok( !!($home and is_dir $home), 'Found our home directory' );
+} else {
+ is( $home, undef, 'Confirmed no home directory' );
+}
+
+# this call is not tested:
+# File::HomeDir->home
+
+# Find this user's home explicitly
+my $my_home = File::HomeDir->my_home;
+if ( $HAVEHOME ) {
+ ok( !!($home and is_dir $home), 'Found our home directory' );
+} else {
+ is( $home, undef, 'Confirmed no home directory' );
+}
+
+# check that $ENV{HOME} is honored if set
+{
+ local $ENV{HOME} = rel2abs('.');
+ is( File::HomeDir->my_home(), $ENV{HOME}, "my_home() returns $ENV{HOME}" );
+}
+
+my $my_home2 = File::HomeDir::my_home();
+if ( $HAVEHOME ) {
+ ok( !!($my_home2 and is_dir $my_home2), 'Found our home directory' );
+} else {
+ is( $home, undef, 'No home directory, as expected' );
+}
+is( $home, $my_home2, 'Different APIs give same results' );
+
+# shall we test using -w if the home directory is writable ?
+
+# Find this user's documents
+SKIP: {
+ my $my_documents = File::HomeDir->my_documents;
+ my $my_documents2 = File::HomeDir::my_documents();
+ is( $my_documents, $my_documents2, 'Different APIs give the same results' );
+
+ skip("Cannot assume existance of documents", 2) unless $HAVEDOCUMENTS;
+ ok( !!($my_documents and is_dir $my_documents), 'Found our documents directory' );
+ ok( !!($my_documents2 and $my_documents2), 'Found our documents directory' );
+}
+
+# Find this user's pictures directory
+SKIP: {
+ skip("Cannot assume existance of pictures", 3) unless $HAVEPICTURES;
+ my $my_pictures = File::HomeDir->my_pictures;
+ my $my_pictures2 = File::HomeDir::my_pictures();
+ is( $my_pictures, $my_pictures2, 'Different APIs give the same results' );
+ ok( !!($my_pictures and is_dir $my_pictures), 'Our pictures directory exists' );
+ ok( !!($my_pictures2 and is_dir $my_pictures2), 'Our pictures directory exists' );
+}
+
+# Find this user's music directory
+SKIP: {
+ skip("Cannot assume existance of music", 3) unless $HAVEMUSIC;
+ my $my_music = File::HomeDir->my_music;
+ my $my_music2 = File::HomeDir::my_music();
+ is( $my_music, $my_music2, 'Different APIs give the same results' );
+ ok( !!($my_music and is_dir $my_music), 'Our music directory exists' );
+ ok( !!($my_music2 and is_dir $my_music2), 'Our music directory exists' );
+}
+
+# Find this user's video directory
+SKIP: {
+ skip("Cannot assume existance of videos", 3) unless $HAVEVIDEOS;
+ my $my_videos = File::HomeDir->my_videos;
+ my $my_videos2 = File::HomeDir::my_videos();
+ is( $my_videos, $my_videos2, 'Different APIs give the same results' );
+ ok( !!($my_videos and is_dir $my_videos), 'Our videos directory exists' );
+ ok( !!($my_videos2 and is_dir $my_videos2), 'Our videos directory exists' );
+}
+
+# Desktop cannot be assumed in all environments
+SKIP: {
+ skip("Cannot assume existance of desktop", 3 ) unless $HAVEDESKTOP;
+
+ # Find this user's desktop data
+ my $my_desktop = File::HomeDir->my_desktop;
+ my $my_desktop2 = File::HomeDir::my_desktop();
+ is( $my_desktop, $my_desktop2, 'Different APIs give the same results' );
+ ok( !!($my_desktop and is_dir $my_desktop), 'Our desktop directory exists' );
+ ok( !!($my_desktop2 and is_dir $my_desktop2), 'Our desktop directory exists' );
+}
+
+# Find this user's local data
+SKIP: {
+ skip("Cannot assume existance of application data", 3) unless $HAVEOTHERS;
+ my $my_data = File::HomeDir->my_data;
+ my $my_data2 = File::HomeDir::my_data();
+ is( $my_data, $my_data2, 'Different APIs give the same results' );
+ ok( !!($my_data and is_dir $my_data), 'Found our local data directory' );
+ ok( !!($my_data2 and is_dir $my_data2), 'Found our local data directory' );
+}
+
+# Shall we check name space pollution by testing functions in main before
+# and after calling use ?
+
+# On platforms other than windows, find root's homedir
+SKIP: {
+ if ( $^O eq 'MSWin32' or $^O eq 'darwin') {
+ skip("Skipping root test on $^O", 1 );
+ }
+
+ # Determine root
+ my $root = getpwuid(0);
+ unless ( $root ) {
+ skip("Skipping, can't determine root", 1 );
+ }
+
+ # Get root's homedir
+ my $root_home1 = home($root);
+ ok( !!($root_home1 and is_dir $root_home1), "Found root's home directory" );
+}
diff --git a/t/10_test.t b/t/10_test.t
new file mode 100644
index 0000000..fc38c30
--- /dev/null
+++ b/t/10_test.t
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+# Testing for the test driver
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+use File::Spec::Functions ':ALL';
+use Test::More tests => 30;
+use File::HomeDir::Test;
+use File::HomeDir;
+
+# Is the test driver enabled?
+is( $File::HomeDir::Test::ENABLED, 1, 'File::HomeDir::Test is enabled' );
+is( $File::HomeDir::IMPLEMENTED_BY, 'File::HomeDir::Test', 'IMPLEMENTED_BY is correct' );
+
+# Was everything hijacked correctly?
+foreach my $method ( qw{
+ my_home
+ my_desktop
+ my_documents
+ my_data
+ my_music
+ my_pictures
+ my_videos
+} ) {
+ my $dir = File::HomeDir->$method();
+ ok( $dir, "$method: Got a directory" );
+ ok( -d $dir, "$method: Directory exists at $dir" );
+ ok( -r $dir, "$method: Directory is readable" );
+ ok( -w $dir, "$method: Directory is writeable" );
+}
diff --git a/t/11_darwin.t b/t/11_darwin.t
new file mode 100644
index 0000000..4318fcc
--- /dev/null
+++ b/t/11_darwin.t
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+
+use Test::More;
+use File::HomeDir;
+
+if ( $File::HomeDir::IMPLEMENTED_BY->isa('File::HomeDir::Darwin') ) {
+ # Force pure perl since it should work everywhere
+ $File::HomeDir::IMPLEMENTED_BY = 'File::HomeDir::Darwin';
+ plan( tests => 9 );
+} else {
+ plan( skip_all => "Not running on Darwin" );
+ exit(0);
+}
+
+SKIP: {
+ my $user;
+ foreach ( 0 .. 9 ) {
+ my $temp = sprintf 'fubar%04d', rand(10000);
+ getpwnam $temp and next;
+ $user = $temp;
+ last;
+ }
+ $user or skip("Unable to find non-existent user", 1);
+ $@ = undef;
+ my $home = eval {File::HomeDir->users_home($user)};
+ $@ and skip("Unable to execute File::HomeDir->users_home('$user')", 1);
+ ok (!defined $home, "Home of non-existent user should be undef");
+}
+
+SCOPE: {
+ # Reality Check
+ my $music = File::HomeDir->my_music;
+ my $videos = File::HomeDir->my_videos;
+ my $pictures = File::HomeDir->my_pictures;
+ my $data = File::HomeDir->my_data;
+ SKIP: {
+ skip( "No music directory", 1 ) unless defined $music;
+ like( $music, qr/Music/ );
+ }
+ SKIP: {
+ skip( "Have music directory", 1 ) if defined $music;
+ is_deeply(
+ [ File::HomeDir->my_music ], [ undef ],
+ "Returns undef in list context",
+ )
+ }
+ SKIP: {
+ skip( "No videos directory", 1 ) unless defined $videos;
+ like( $videos, qr/Movies/ );
+ }
+ SKIP: {
+ skip( "Have videos directory", 1 ) if defined $videos;
+ is_deeply(
+ [ File::HomeDir->my_videos ], [ undef ],
+ "Returns undef in list context",
+ )
+ }
+ SKIP: {
+ skip( "No pictures directory", 1 ) unless defined $pictures;
+ like( $pictures, qr/Pictures/ );
+ }
+ SKIP: {
+ skip( "Have pictures directory", 1 ) if defined $pictures;
+ is_deeply(
+ [ File::HomeDir->my_pictures ], [ undef ],
+ "Returns undef in list context",
+ )
+ }
+ SKIP: {
+ skip( "No application support directory", 1 ) unless defined $data;
+ like( $data, qr/Application Support/ );
+ }
+ SKIP: {
+ skip( "Have data directory", 1 ) if defined $data;
+ is_deeply(
+ [ File::HomeDir->my_data ], [ undef ],
+ "Returns undef in list context",
+ )
+ }
+}
diff --git a/t/12_darwin_carbon.t b/t/12_darwin_carbon.t
new file mode 100644
index 0000000..2ef80e1
--- /dev/null
+++ b/t/12_darwin_carbon.t
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+
+use Test::More;
+use File::HomeDir;
+
+if ( $File::HomeDir::IMPLEMENTED_BY->isa('File::HomeDir::Darwin::Carbon') ) {
+ plan( tests => 5 );
+} else {
+ plan( skip_all => "Not running on 32-bit Darwin" );
+ exit(0);
+}
+
+SKIP: {
+ my $user;
+ foreach (0 .. 9) {
+ my $temp = sprintf 'fubar%04d', rand(10000);
+ getpwnam $temp and next;
+ $user = $temp;
+ last;
+ }
+ $user or skip("Unable to find non-existent user", 1);
+ $@ = undef;
+ my $home = eval {File::HomeDir->users_home($user)};
+ $@ and skip("Unable to execute File::HomeDir->users_home('$user')");
+ ok (!defined $home, "Home of non-existent user should be undef");
+}
+
+# CPAN Testers results suggest we can't reasonably assume these directories
+# will always exist
+SKIP: {
+ my $dir = File::HomeDir->my_music;
+ unless ( defined $dir ) {
+ skip( "Testing user does not have a Music directory", 1 );
+ }
+ like( $dir, qr/Music/ );
+}
+SKIP: {
+ my $dir = File::HomeDir->my_videos;
+ unless ( defined $dir ) {
+ skip( "Testing user does not have a Movies directory", 1 );
+ }
+ like( $dir, qr/Movies/ );
+}
+SKIP: {
+ my $dir = File::HomeDir->my_pictures;
+ unless ( defined $dir ) {
+ skip( "Testing user does not have a Pictures directory", 1 );
+ }
+ like( $dir, qr/Pictures/ );
+}
+
+SKIP: {
+ my $data = File::HomeDir->my_data;
+ skip( "No application support directory", 1 ) unless defined $data;
+ like( $data, qr/Application Support/ );
+}
diff --git a/t/13_darwin_cocoa.t b/t/13_darwin_cocoa.t
new file mode 100644
index 0000000..a22f9aa
--- /dev/null
+++ b/t/13_darwin_cocoa.t
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+
+use Test::More;
+use File::HomeDir;
+
+if (
+ $File::HomeDir::IMPLEMENTED_BY->isa('File::HomeDir::Darwin')
+ and
+ eval "require Mac::SystemDirectory; 1"
+ ) {
+ # Force Cocoa if you have Mac::SystemDirectory
+ require File::HomeDir::Darwin::Cocoa;
+ $File::HomeDir::IMPLEMENTED_BY = 'File::HomeDir::Darwin::Cocoa';
+ plan( tests => 5 );
+} else {
+ plan( skip_all => "Not running on Darwin with Cocoa API using Mac::SystemDirectory" );
+ exit(0);
+}
+
+SKIP: {
+ my $user;
+ foreach ( 0 .. 9 ) {
+ my $temp = sprintf 'fubar%04d', rand(10000);
+ getpwnam $temp and next;
+ $user = $temp;
+ last;
+ }
+ $user or skip("Unable to find non-existent user", 1);
+ $@ = undef;
+ my $home = eval { File::HomeDir->users_home($user) };
+ $@ and skip("Unable to execute File::HomeDir->users_home('$user')", 1);
+ ok (!defined $home, "Home of non-existent user should be undef");
+}
+
+SCOPE: {
+ # Reality Check
+ my $music = File::HomeDir->my_music;
+ my $video = File::HomeDir->my_videos;
+ my $pictures = File::HomeDir->my_pictures;
+ SKIP: {
+ skip( "No music directory", 1 ) unless defined $music;
+ like( File::HomeDir->my_music, qr/Music/ );
+ }
+ SKIP: {
+ skip( "No videos directory", 1 ) unless defined $video;
+ like( File::HomeDir->my_videos, qr/Movies/ );
+ }
+ SKIP: {
+ skip( "No pictures directory", 1 ) unless defined $pictures;
+ like( File::HomeDir->my_pictures, qr/Pictures/ );
+ }
+
+ SKIP: {
+ my $data = File::HomeDir->my_data;
+ skip( "No application support directory", 1 ) unless defined $data;
+ like( $data, qr/Application Support/ );
+ }
+}
diff --git a/xt/meta.t b/xt/meta.t
new file mode 100644
index 0000000..2f8b2c7
--- /dev/null
+++ b/xt/meta.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+# Test that our META.yml file matches the current specification.
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+
+my $MODULE = 'Test::CPAN::Meta 0.17';
+
+# Don't run tests for installs
+use Test::More;
+unless ( $ENV{AUTOMATED_TESTING} or $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+# Load the testing module
+eval "use $MODULE";
+if ( $@ ) {
+ $ENV{RELEASE_TESTING}
+ ? die( "Failed to load required release-testing module $MODULE" )
+ : plan( skip_all => "$MODULE not available for testing" );
+}
+
+meta_yaml_ok();
diff --git a/xt/pmv.t b/xt/pmv.t
new file mode 100644
index 0000000..f285be3
--- /dev/null
+++ b/xt/pmv.t
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+# Test that our declared minimum Perl version matches our syntax
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+
+my @MODULES = (
+ 'Perl::MinimumVersion 1.27',
+ 'Test::MinimumVersion 0.101080',
+);
+
+# Don't run tests for installs
+use Test::More;
+unless ( $ENV{AUTOMATED_TESTING} or $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+# Load the testing modules
+foreach my $MODULE ( @MODULES ) {
+ eval "use $MODULE";
+ if ( $@ ) {
+ $ENV{RELEASE_TESTING}
+ ? die( "Failed to load required release-testing module $MODULE" )
+ : plan( skip_all => "$MODULE not available for testing" );
+ }
+}
+
+all_minimum_version_from_metayml_ok();
diff --git a/xt/pod.t b/xt/pod.t
new file mode 100644
index 0000000..170cae0
--- /dev/null
+++ b/xt/pod.t
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+# Test that the syntax of our POD documentation is valid
+
+use strict;
+BEGIN {
+ $| = 1;
+ $^W = 1;
+}
+
+my @MODULES = (
+ 'Pod::Simple 3.14',
+ 'Test::Pod 1.44',
+);
+
+# Don't run tests for installs
+use Test::More;
+unless ( $ENV{AUTOMATED_TESTING} or $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+# Load the testing modules
+foreach my $MODULE ( @MODULES ) {
+ eval "use $MODULE";
+ if ( $@ ) {
+ $ENV{RELEASE_TESTING}
+ ? die( "Failed to load required release-testing module $MODULE" )
+ : plan( skip_all => "$MODULE not available for testing" );
+ }
+}
+
+all_pod_files_ok();