File: | local/lib/perl5/File/HomeDir.pm |
Coverage: | 22.4% |
line | stmt | bran | cond | sub | time | code |
---|---|---|---|---|---|---|
1 | package File::HomeDir; | |||||
2 | ||||||
3 | # See POD at end for documentation | |||||
4 | ||||||
5 | 2 2 | 22 4 | use 5.00503; | |||
6 | 2 2 2 | 7 0 30 | use strict; | |||
7 | 2 2 2 | 5 2 10 | use Carp (); | |||
8 | 2 2 2 | 5 1 11 | use Config (); | |||
9 | 2 2 2 | 3 2 9 | use File::Spec (); | |||
10 | 2 2 2 | 440 3 29 | use File::Which (); | |||
11 | ||||||
12 | # Globals | |||||
13 | 2 2 2 | 7 2 128 | use vars qw{$VERSION @ISA @EXPORT @EXPORT_OK $IMPLEMENTED_BY}; | |||
14 | BEGIN { | |||||
15 | 2 | 2 | $VERSION = '1.00'; | |||
16 | ||||||
17 | # Inherit manually | |||||
18 | 2 | 5 | require Exporter; | |||
19 | 2 | 10 | @ISA = qw{ Exporter }; | |||
20 | 2 | 3 | @EXPORT = qw{ home }; | |||
21 | 2 | 1305 | @EXPORT_OK = qw{ | |||
22 | home | |||||
23 | my_home | |||||
24 | my_desktop | |||||
25 | my_documents | |||||
26 | my_music | |||||
27 | my_pictures | |||||
28 | my_videos | |||||
29 | my_data | |||||
30 | my_dist_config | |||||
31 | my_dist_data | |||||
32 | users_home | |||||
33 | users_desktop | |||||
34 | users_documents | |||||
35 | users_music | |||||
36 | users_pictures | |||||
37 | users_videos | |||||
38 | users_data | |||||
39 | }; | |||||
40 | ||||||
41 | # %~ doesn't need (and won't take) exporting, as it's a magic | |||||
42 | # symbol name that's always looked for in package 'main'. | |||||
43 | } | |||||
44 | ||||||
45 | # Inlined Params::Util functions | |||||
46 | sub _CLASS ($) { | |||||
47 | 2 | 68 | (defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s) ? $_[0] : undef; | |||
48 | } | |||||
49 | sub _DRIVER ($$) { | |||||
50 | 2 | 4 | (defined _CLASS($_[0]) and eval "require $_[0];" and ! $@ and $_[0]->isa($_[1]) and $_[0] ne $_[1]) ? $_[0] : undef; | |||
51 | } | |||||
52 | ||||||
53 | # Platform detection | |||||
54 | if ( $IMPLEMENTED_BY ) { | |||||
55 | # Allow for custom HomeDir classes | |||||
56 | # Leave it as the existing value | |||||
57 | } elsif ( $^O eq 'MSWin32' ) { | |||||
58 | # All versions of Windows | |||||
59 | $IMPLEMENTED_BY = 'File::HomeDir::Windows'; | |||||
60 | } elsif ( $^O eq 'darwin') { | |||||
61 | # 1st: try Mac::SystemDirectory by chansen | |||||
62 | if ( eval { require Mac::SystemDirectory; 1 } ) { | |||||
63 | $IMPLEMENTED_BY = 'File::HomeDir::Darwin::Cocoa'; | |||||
64 | } elsif ( eval { require Mac::Files; 1 } ) { | |||||
65 | # 2nd try Mac::Files: Carbon - unmaintained since 2006 except some 64bit fixes | |||||
66 | $IMPLEMENTED_BY = 'File::HomeDir::Darwin::Carbon'; | |||||
67 | } else { | |||||
68 | # 3rd: fallback: pure perl | |||||
69 | $IMPLEMENTED_BY = 'File::HomeDir::Darwin'; | |||||
70 | } | |||||
71 | } elsif ( $^O eq 'MacOS' ) { | |||||
72 | # Legacy Mac OS | |||||
73 | $IMPLEMENTED_BY = 'File::HomeDir::MacOS9'; | |||||
74 | } elsif ( File::Which::which('xdg-user-dir') ) { | |||||
75 | # freedesktop unixes | |||||
76 | $IMPLEMENTED_BY = 'File::HomeDir::FreeDesktop'; | |||||
77 | } else { | |||||
78 | # Default to Unix semantics | |||||
79 | $IMPLEMENTED_BY = 'File::HomeDir::Unix'; | |||||
80 | } | |||||
81 | unless ( _DRIVER($IMPLEMENTED_BY, 'File::HomeDir::Driver') ) { | |||||
82 | Carp::croak("Missing or invalid File::HomeDir driver $IMPLEMENTED_BY"); | |||||
83 | } | |||||
84 | ||||||
85 | ||||||
86 | ||||||
87 | ||||||
88 | ||||||
89 | ##################################################################### | |||||
90 | # Current User Methods | |||||
91 | ||||||
92 | sub my_home { | |||||
93 | 2 | 9 | $IMPLEMENTED_BY->my_home; | |||
94 | } | |||||
95 | ||||||
96 | sub my_desktop { | |||||
97 | 0 | 0 | $IMPLEMENTED_BY->can('my_desktop') | |||
98 | ? $IMPLEMENTED_BY->my_desktop | |||||
99 | : Carp::croak("The my_desktop method is not implemented on this platform"); | |||||
100 | } | |||||
101 | ||||||
102 | sub my_documents { | |||||
103 | 0 | 0 | $IMPLEMENTED_BY->can('my_documents') | |||
104 | ? $IMPLEMENTED_BY->my_documents | |||||
105 | : Carp::croak("The my_documents method is not implemented on this platform"); | |||||
106 | } | |||||
107 | ||||||
108 | sub my_music { | |||||
109 | 0 | 0 | $IMPLEMENTED_BY->can('my_music') | |||
110 | ? $IMPLEMENTED_BY->my_music | |||||
111 | : Carp::croak("The my_music method is not implemented on this platform"); | |||||
112 | } | |||||
113 | ||||||
114 | sub my_pictures { | |||||
115 | 0 | 0 | $IMPLEMENTED_BY->can('my_pictures') | |||
116 | ? $IMPLEMENTED_BY->my_pictures | |||||
117 | : Carp::croak("The my_pictures method is not implemented on this platform"); | |||||
118 | } | |||||
119 | ||||||
120 | sub my_videos { | |||||
121 | 0 | 0 | $IMPLEMENTED_BY->can('my_videos') | |||
122 | ? $IMPLEMENTED_BY->my_videos | |||||
123 | : Carp::croak("The my_videos method is not implemented on this platform"); | |||||
124 | } | |||||
125 | ||||||
126 | sub my_data { | |||||
127 | 0 | 0 | $IMPLEMENTED_BY->can('my_data') | |||
128 | ? $IMPLEMENTED_BY->my_data | |||||
129 | : Carp::croak("The my_data method is not implemented on this platform"); | |||||
130 | } | |||||
131 | ||||||
132 | ||||||
133 | sub my_dist_data { | |||||
134 | 0 | 0 | my $params = ref $_[-1] eq 'HASH' ? pop : {}; | |||
135 | 0 | 0 | my $dist = pop or Carp::croak("The my_dist_data method requires an argument"); | |||
136 | 0 | 0 | my $data = my_data(); | |||
137 | ||||||
138 | # If datadir is not defined, there's nothing we can do: bail out | |||||
139 | # and return nothing... | |||||
140 | 0 | 0 | return undef unless defined $data; | |||
141 | ||||||
142 | # On traditional unixes, hide the top-level directory | |||||
143 | 0 | 0 | my $var = $data eq home() | |||
144 | ? File::Spec->catdir( $data, '.perl', 'dist', $dist ) | |||||
145 | : File::Spec->catdir( $data, 'Perl', 'dist', $dist ); | |||||
146 | ||||||
147 | # directory exists: return it | |||||
148 | 0 | 0 | return $var if -d $var; | |||
149 | ||||||
150 | # directory doesn't exist: check if we need to create it... | |||||
151 | 0 | 0 | return undef unless $params->{create}; | |||
152 | ||||||
153 | # user requested directory creation | |||||
154 | 0 | 0 | require File::Path; | |||
155 | 0 | 0 | File::Path::mkpath( $var ); | |||
156 | 0 | 0 | return $var; | |||
157 | } | |||||
158 | ||||||
159 | sub my_dist_config { | |||||
160 | 0 | 0 | my $params = ref $_[-1] eq 'HASH' ? pop : {}; | |||
161 | 0 | 0 | my $dist = pop or Carp::croak("The my_dist_config method requires an argument"); | |||
162 | ||||||
163 | # not all platforms support a specific my_config() method | |||||
164 | 0 | 0 | my $config = $IMPLEMENTED_BY->can('my_config') | |||
165 | ? $IMPLEMENTED_BY->my_config | |||||
166 | : $IMPLEMENTED_BY->my_documents; | |||||
167 | ||||||
168 | # If neither configdir nor my_documents is defined, there's | |||||
169 | # nothing we can do: bail out and return nothing... | |||||
170 | 0 | 0 | return undef unless defined $config; | |||
171 | ||||||
172 | # On traditional unixes, hide the top-level dir | |||||
173 | 0 | 0 | my $etc = $config eq home() | |||
174 | ? File::Spec->catdir( $config, '.perl', $dist ) | |||||
175 | : File::Spec->catdir( $config, 'Perl', $dist ); | |||||
176 | ||||||
177 | # directory exists: return it | |||||
178 | 0 | 0 | return $etc if -d $etc; | |||
179 | ||||||
180 | # directory doesn't exist: check if we need to create it... | |||||
181 | 0 | 0 | return undef unless $params->{create}; | |||
182 | ||||||
183 | # user requested directory creation | |||||
184 | 0 | 0 | require File::Path; | |||
185 | 0 | 0 | File::Path::mkpath( $etc ); | |||
186 | 0 | 0 | return $etc; | |||
187 | } | |||||
188 | ||||||
189 | ||||||
190 | ||||||
191 | ||||||
192 | ##################################################################### | |||||
193 | # General User Methods | |||||
194 | ||||||
195 | sub users_home { | |||||
196 | 0 | 0 | $IMPLEMENTED_BY->can('users_home') | |||
197 | ? $IMPLEMENTED_BY->users_home( $_[-1] ) | |||||
198 | : Carp::croak("The users_home method is not implemented on this platform"); | |||||
199 | } | |||||
200 | ||||||
201 | sub users_desktop { | |||||
202 | 0 | 0 | $IMPLEMENTED_BY->can('users_desktop') | |||
203 | ? $IMPLEMENTED_BY->users_desktop( $_[-1] ) | |||||
204 | : Carp::croak("The users_desktop method is not implemented on this platform"); | |||||
205 | } | |||||
206 | ||||||
207 | sub users_documents { | |||||
208 | 0 | 0 | $IMPLEMENTED_BY->can('users_documents') | |||
209 | ? $IMPLEMENTED_BY->users_documents( $_[-1] ) | |||||
210 | : Carp::croak("The users_documents method is not implemented on this platform"); | |||||
211 | } | |||||
212 | ||||||
213 | sub users_music { | |||||
214 | 0 | 0 | $IMPLEMENTED_BY->can('users_music') | |||
215 | ? $IMPLEMENTED_BY->users_music( $_[-1] ) | |||||
216 | : Carp::croak("The users_music method is not implemented on this platform"); | |||||
217 | } | |||||
218 | ||||||
219 | sub users_pictures { | |||||
220 | 0 | 0 | $IMPLEMENTED_BY->can('users_pictures') | |||
221 | ? $IMPLEMENTED_BY->users_pictures( $_[-1] ) | |||||
222 | : Carp::croak("The users_pictures method is not implemented on this platform"); | |||||
223 | } | |||||
224 | ||||||
225 | sub users_videos { | |||||
226 | 0 | 0 | $IMPLEMENTED_BY->can('users_videos') | |||
227 | ? $IMPLEMENTED_BY->users_videos( $_[-1] ) | |||||
228 | : Carp::croak("The users_videos method is not implemented on this platform"); | |||||
229 | } | |||||
230 | ||||||
231 | sub users_data { | |||||
232 | 0 | 0 | $IMPLEMENTED_BY->can('users_data') | |||
233 | ? $IMPLEMENTED_BY->users_data( $_[-1] ) | |||||
234 | : Carp::croak("The users_data method is not implemented on this platform"); | |||||
235 | } | |||||
236 | ||||||
237 | ||||||
238 | ||||||
239 | ||||||
240 | ||||||
241 | ##################################################################### | |||||
242 | # Legacy Methods | |||||
243 | ||||||
244 | # Find the home directory of an arbitrary user | |||||
245 | sub home (;$) { | |||||
246 | # Allow to be called as a method | |||||
247 | 0 | 0 | if ( $_[0] and $_[0] eq 'File::HomeDir' ) { | |||
248 | 0 | 0 | shift(); | |||
249 | } | |||||
250 | ||||||
251 | # No params means my home | |||||
252 | 0 | 0 | return my_home() unless @_; | |||
253 | ||||||
254 | # Check the param | |||||
255 | 0 | 0 | my $name = shift; | |||
256 | 0 | 0 | if ( ! defined $name ) { | |||
257 | 0 | 0 | Carp::croak("Can't use undef as a username"); | |||
258 | } | |||||
259 | 0 | 0 | if ( ! length $name ) { | |||
260 | 0 | 0 | Carp::croak("Can't use empty-string (\"\") as a username"); | |||
261 | } | |||||
262 | ||||||
263 | # A dot also means my home | |||||
264 | ### Is this meant to mean File::Spec->curdir? | |||||
265 | 0 | 0 | if ( $name eq '.' ) { | |||
266 | 0 | 0 | return my_home(); | |||
267 | } | |||||
268 | ||||||
269 | # Now hand off to the implementor | |||||
270 | 0 | 0 | $IMPLEMENTED_BY->users_home($name); | |||
271 | } | |||||
272 | ||||||
273 | ||||||
274 | ||||||
275 | ||||||
276 | ||||||
277 | ##################################################################### | |||||
278 | # Tie-Based Interface | |||||
279 | ||||||
280 | # Okay, things below this point get scary | |||||
281 | ||||||
282 | CLASS: { | |||||
283 | # Make the class for the %~ tied hash: | |||||
284 | package File::HomeDir::TIE; | |||||
285 | ||||||
286 | # Make the singleton object. | |||||
287 | # (We don't use the hash for anything, though) | |||||
288 | ### THEN WHY MAKE IT??? | |||||
289 | my $SINGLETON = bless {}; | |||||
290 | ||||||
291 | 2 | 2 | sub TIEHASH { $SINGLETON } | |||
292 | ||||||
293 | sub FETCH { | |||||
294 | # Catch a bad username | |||||
295 | 0 | unless ( defined $_[1] ) { | ||||
296 | 0 | Carp::croak("Can't use undef as a username"); | ||||
297 | } | |||||
298 | ||||||
299 | # Get our homedir | |||||
300 | 0 | unless ( length $_[1] ) { | ||||
301 | 0 | return File::HomeDir::my_home(); | ||||
302 | } | |||||
303 | ||||||
304 | # Get a named user's homedir | |||||
305 | 0 | Carp::carp("The tied %~ hash has been deprecated"); | ||||
306 | 0 | return File::HomeDir::home($_[1]); | ||||
307 | } | |||||
308 | ||||||
309 | 0 | sub STORE { _bad('STORE') } | ||||
310 | 0 | sub EXISTS { _bad('EXISTS') } | ||||
311 | 0 | sub DELETE { _bad('DELETE') } | ||||
312 | 0 | sub CLEAR { _bad('CLEAR') } | ||||
313 | 0 | sub FIRSTKEY { _bad('FIRSTKEY') } | ||||
314 | 0 | sub NEXTKEY { _bad('NEXTKEY') } | ||||
315 | ||||||
316 | sub _bad ($) { | |||||
317 | 0 | Carp::croak("You can't $_[0] with the %~ hash") | ||||
318 | } | |||||
319 | } | |||||
320 | ||||||
321 | # Do the actual tie of the global %~ variable | |||||
322 | tie %~, 'File::HomeDir::TIE'; | |||||
323 | ||||||
324 | 1; | |||||
325 |