File Coverage

blib/lib/File/XDG.pm
Criterion Covered Total %
statement 105 128 82.0
branch 42 60 70.0
condition 15 41 36.5
subroutine 25 26 96.1
pod 13 13 100.0
total 200 268 74.6


line stmt bran cond sub pod time code
1             package File::XDG;
2              
3 1     1   123874 use strict;
  1         3  
  1         42  
4 1     1   6 use warnings;
  1         27  
  1         59  
5 1     1   5 use Carp ();
  1         2  
  1         18  
6 1     1   6 use Config;
  1         1  
  1         46  
7 1     1   574 use Ref::Util qw( is_coderef is_arrayref );
  1         2823  
  1         121  
8 1     1   8 use if $^O eq 'MSWin32', 'Win32';
  1         2  
  1         2241  
9              
10             # ABSTRACT: Basic implementation of the XDG base directory specification
11             our $VERSION = '1.03'; # VERSION
12              
13              
14              
15              
16             sub new {
17 17     17 1 513256 my $class = shift;
18 17         82 my %args = (@_);
19              
20 17         50 my $name = delete $args{name};
21 17 50       84 Carp::croak('application name required') unless defined $name;
22              
23 17         38 my $api = delete $args{api};
24 17 100       55 $api = 0 unless defined $api;
25 17 50 66     76 Carp::croak("Unsupported api = $api") unless $api == 0 || $api == 1;
26              
27 17         36 my $path_class = delete $args{path_class};
28              
29 17 100       51 unless(defined $path_class) {
30 12 100       34 if($api >= 1) {
31 3         7 $path_class = 'Path::Tiny';
32             } else {
33 9         17 $path_class = 'Path::Class';
34             }
35             }
36              
37 17 100       134 my $file_class = $path_class eq 'Path::Class' ? 'Path::Class::File' : $path_class;
38 17 100       57 my $dir_class = $path_class eq 'Path::Class' ? 'Path::Class::Dir' : $path_class;
39              
40 17 100       95 if(is_coderef($path_class))
    100          
    100          
    100          
    50          
41             {
42 1         5 $dir_class = $file_class = $path_class;
43             }
44             elsif(is_arrayref($path_class))
45             {
46 1         5 ($file_class, $dir_class) = @$path_class;
47             }
48             elsif($path_class eq 'Path::Tiny')
49             {
50 4         40 require Path::Tiny;
51             }
52             elsif($path_class eq 'Path::Class')
53             {
54 10         100 require Path::Class::File;
55 10         40 require Path::Class::Dir;
56             }
57             elsif($path_class eq 'File::Spec')
58             {
59 1         10 require File::Spec;
60 1     1   7 $file_class = sub { File::Spec->catfile(@_) };
  1         84  
61 1     1   4 $dir_class = sub { File::Spec->catdir(@_) };
  1         17  
62             }
63             else
64             {
65 0         0 Carp::croak("Unknown path class: $path_class");
66             }
67              
68 17         45 my $strict = delete $args{strict};
69 17 50 33     117 Carp::croak("XDG base directory specification cannot strictly implemented on Windows")
70             if $^O eq 'MSWin32' && $strict;
71              
72 17 50       50 Carp::croak("unknown arguments: @{[ sort keys %args ]}") if %args;
  0         0  
73              
74             my $self = bless {
75             name => $name,
76             api => $api,
77             file_class => $file_class,
78             dir_class => $dir_class,
79             strict => $strict,
80             runtime => $ENV{XDG_RUNTIME_DIR},
81 17         163 }, $class;
82              
83 17 50       104 if($^O eq 'MSWin32') {
84 0         0 my $local = Win32::GetFolderPath(Win32::CSIDL_LOCAL_APPDATA(), 1);
85 0         0 $self->{home} = $local;
86 0   0     0 $self->{data} = $ENV{XDG_DATA_HOME} || "$local\\.local\\share\\";
87 0   0     0 $self->{cache} = $ENV{XDG_CACHE_HOME} || "$local\\.cache\\";
88 0   0     0 $self->{config} = $ENV{XDG_CONFIG_HOME} || "$local\\.config\\";
89 0   0     0 $self->{state} = $ENV{XDG_STATE_HOME} || "$local\\.local\\state\\";
90 0   0     0 $self->{data_dirs} = $ENV{XDG_DATA_DIRS} || '';
91 0   0     0 $self->{config_dirs} = $ENV{XDG_CONFIG_DIRS} || '';
92             } else {
93 17   33     63 my $home = $ENV{HOME} || [getpwuid($>)]->[7];
94 17         57 $self->{home} = $home;
95 17   66     116 $self->{data} = $ENV{XDG_DATA_HOME} || "$home/.local/share/";
96 17   66     100 $self->{cache} = $ENV{XDG_CACHE_HOME} || "$home/.cache/";
97 17   33     94 $self->{state} = $ENV{XDG_STATE_HOME} || "$home/.local/state/";
98 17   66     122 $self->{config} = $ENV{XDG_CONFIG_HOME} || "$home/.config/";
99 17   100     74 $self->{data_dirs} = $ENV{XDG_DATA_DIRS} || '/usr/local/share:/usr/share';
100 17   100     77 $self->{config_dirs} = $ENV{XDG_CONFIG_DIRS} || '/etc/xdg';
101             }
102              
103 17         98 return $self;
104             }
105              
106             sub _dir {
107 26     26   7642 my $self = shift;
108             is_coderef($self->{dir_class})
109             ? $self->{dir_class}->(@_)
110 26 100       205 : $self->{dir_class}->new(@_);
111             }
112              
113             sub _file {
114 31     31   82 my $self = shift;
115             is_coderef($self->{dir_class})
116             ? $self->{file_class}->(@_)
117 31 100       199 : $self->{file_class}->new(@_);
118             }
119              
120             sub _dirs {
121 24     24   64 my($self, $type) = @_;
122 24 50       230 return $self->{"${type}_dirs"} if exists $self->{"${type}_dirs"};
123 0         0 Carp::croak('invalid _dirs requested');
124             }
125              
126             sub _lookup_file {
127 12     12   35 my ($self, $type, @subpath) = @_;
128              
129 12 50       69 Carp::croak('subpath not specified') unless @subpath;
130 12 50       42 Carp::croak("invalid type: $type") unless defined $self->{$type};
131              
132 12         236 my @dirs = ($self->{$type}, split(/\Q$Config{path_sep}\E/, $self->_dirs($type)));
133 12         49 my @paths = map { $self->_file($_, @subpath) } @dirs;
  24         1172  
134 12         823 my ($match) = grep { -f $_ } @paths;
  24         655  
135              
136 12         592 return $match;
137             }
138              
139              
140             sub data_home {
141 4     4 1 4542 my $self = shift;
142 4         10 my $xdg = $self->{data};
143 4         14 return $self->_dir($xdg, $self->{name});
144             }
145              
146              
147             sub config_home {
148 4     4 1 1440 my $self = shift;
149 4         9 my $xdg = $self->{config};
150 4         17 return $self->_dir($xdg, $self->{name});
151             }
152              
153              
154             sub cache_home {
155 2     2 1 2666 my $self = shift;
156 2         5 my $xdg = $self->{cache};
157 2         9 return $self->_dir($xdg, $self->{name});
158             }
159              
160              
161             sub state_home {
162 0     0 1 0 my $self = shift;
163 0         0 return $self->_dir($self->{state}, $self->{name});
164             }
165              
166              
167             sub runtime_home
168             {
169 2     2 1 6 my($self) = @_;
170 2         8 my $base = $self->_runtime_dir;
171 2 100       13 defined $base ? $self->_dir($base, $self->{name}) : undef;
172             }
173              
174             sub _runtime_dir
175             {
176 2     2   4 my($self) = @_;
177 2 100       8 if(defined $self->{runtime})
178             {
179 1         5 return $self->{runtime};
180             }
181              
182             # the spec says only to look for the environment variable
183 1 50       5 return undef if $self->{strict};
184              
185 0         0 my @maybe;
186              
187 0 0       0 if($^O eq 'linux')
188             {
189 0         0 push @maybe, "/run/user/$<";
190             }
191              
192 0         0 foreach my $maybe (@maybe)
193             {
194             # if we are going rogue and trying to find the runtime dir
195             # on our own, then we hould at least check that the directory
196             # fufills the requirements of the spec: directory, owned by
197             # us, with permission of 0700.
198 0 0       0 next unless -d $maybe;
199 0 0       0 next unless -o $maybe;
200 0         0 my $perm = [stat $maybe]->[2] & oct('0777');
201 0 0       0 next unless $perm == oct('0700');
202 0         0 return $maybe;
203             }
204              
205 0         0 return undef;
206             }
207              
208              
209             sub data_dirs {
210 6     6 1 4870 return shift->_dirs('data');
211             }
212              
213              
214             sub data_dirs_list {
215 2     2 1 7 my $self = shift;
216 2         48 return map { $self->_dir($_) } split /\Q$Config{path_sep}\E/, $self->data_dirs;
  4         128  
217             }
218              
219              
220             sub config_dirs {
221 6     6 1 244 return shift->_dirs('config');
222             }
223              
224              
225             sub config_dirs_list {
226 2     2 1 3927 my $self = shift;
227 2         34 return map { $self->_dir($_) } split /\Q$Config{path_sep}\E/, $self->config_dirs;
  3         81  
228             }
229              
230              
231             sub exe_dir
232             {
233 2     2 1 11 my($self) = @_;
234 2         75 -d "@{[ $self->{home} ]}/.local/bin"
235 2 100       4 ? $self->_dir($self->{home}, '.local', 'bin')
236             : undef;
237             }
238              
239              
240             sub lookup_data_file {
241 6     6 1 12844 my ($self, @subpath) = @_;
242 6 100       33 unshift @subpath, $self->{name} if $self->{api} >= 1;
243 6         19 return $self->_lookup_file('data', @subpath);
244             }
245              
246              
247             sub lookup_config_file {
248 6     6 1 9326 my ($self, @subpath) = @_;
249 6 100       35 unshift @subpath, $self->{name} if $self->{api} >= 1;
250 6         40 return $self->_lookup_file('config', @subpath);
251             }
252              
253              
254             1;
255              
256             __END__
257              
258             =pod
259              
260             =encoding UTF-8
261              
262             =head1 NAME
263              
264             File::XDG - Basic implementation of the XDG base directory specification
265              
266             =head1 VERSION
267              
268             version 1.03
269              
270             =head1 SYNOPSIS
271              
272             use File::XDG 1.00;
273            
274             my $xdg = File::XDG->new( name => 'foo', api => 1 );
275            
276             # user config
277             my $path = $xdg->config_home;
278            
279             # user data
280             my $path = $xdg->data_home;
281            
282             # user cache
283             my $path = $xdg->cache_home;
284            
285             # system $config
286             my @dirs = $xdg->config_dirs_list;
287            
288             # system data
289             my @dirs = $xdg->data_dirs_list;
290              
291             =head1 DESCRIPTION
292              
293             This module provides a basic implementation of the XDG base directory
294             specification as exists by the Free Desktop Organization (FDO). It supports
295             all XDG directories except for the runtime directories, which require session
296             management support in order to function.
297              
298             =head1 CONSTRUCTOR
299              
300             =head2 new
301              
302             my $xdg = File::XDG->new( %args );
303              
304             Returns a new instance of a L<File::XDG> object. This must be called with an
305             application name as the L</name> argument.
306              
307             Takes the following named arguments:
308              
309             =over 4
310              
311             =item api
312              
313             [version 0.09]
314              
315             The API version to use.
316              
317             =over 4
318              
319             =item api = 0
320              
321             The default and original API version. For backward compatibility only.
322              
323             =item api = 1
324              
325             Recommended stable API version for all new code.
326              
327             =back
328              
329             =item name
330              
331             Name of the application for which File::XDG is being used.
332              
333             =item path_class
334              
335             [version 0.09]
336              
337             The path class to return
338              
339             =over
340              
341             =item L<File::Spec>
342              
343             All methods that return a file will return a string generated by L<File::Spec>.
344              
345             =item L<Path::Class>
346              
347             This is the default with api = 0. All methods that return a file will return
348             an instance of L<Path::Class::File> and all methods that return a directory will
349             return an instance of L<Path::Class::Dir>.
350              
351             =item L<Path::Tiny>
352              
353             This is the default with api = 1. All methods that return a file will return
354             an instance of L<Path::Tiny>.
355              
356             =item C<CODEREF>
357              
358             If a code reference is passed in then this will be called in order to construct
359             the path class. This allows rolling your own customer path class objects.
360             Example:
361              
362             my $xdg = File::XDG->new(
363             name => 'foo',
364             # equivalent to path_class => 'Path::Tiny'
365             path_class => sub { Path::Tiny->new(@_),
366             );
367              
368             =item C<ARRAY>
369              
370             Similar to passing a code reference, an array reference with two code references
371             means the first code reference will be used for file paths and the second will
372             be used for directory paths. This is for path classes that differentiate
373             between files and directories.
374              
375             # equivalent to path_class => 'Path::Class'
376             my $xdg = File::XDG->new(
377             name => 'foo',
378             path_class => [
379             sub { Path::Class::File->new(@_) ),
380             sub { Path::Class::Dir->new(@_) },
381             ],
382             );
383              
384             =back
385              
386             =item strict
387              
388             [version 0.10]
389              
390             More strictly follow the XDG base directory specification. In particular
391              
392             =over 4
393              
394             =item
395              
396             On Windows a an exception will be thrown when creating the L<File::XDG>
397             object because the spec cannot correctly be implemented.
398              
399             Historically this module has made some useful assumptions like using
400             C<;> instead of C<:> for the path separator character. This breaks the
401             spec.
402              
403             =item
404              
405             On some systems, this module will look in system specific locations for
406             the L</runtime_home>. This is useful, but technically violates the spec,
407             so under strict mode the L</runtime_home> method will only return a path
408             if one can be found via the spec.
409              
410             =back
411              
412             =back
413              
414             =head1 METHODS
415              
416             =head2 data_home
417              
418             my $path = $xdg->data_home;
419              
420             Returns the user-specific data directory for the application as a path class object.
421              
422             =head2 config_home
423              
424             my $path = $xdg->config_home;
425              
426             Returns the user-specific configuration directory for the application as a path class object.
427              
428             =head2 cache_home
429              
430             my $path = $xdg->cache_home;
431              
432             Returns the user-specific cache directory for the application as a path class object.
433              
434             =head2 state_home
435              
436             my $path = $xdg->state_home;
437              
438             Returns the user-specific state directory for the application as a path class object.
439              
440             =head2 runtime_home
441              
442             [version 0.10]
443              
444             my $dir = $xdg->runtime_home;
445              
446             Returns the directory for user-specific non-essential runtime files and other file objects
447             (such as sockets, named pipes, etc) for the application.
448              
449             This is not always provided, if not available, this method will return C<undef>.
450              
451             Under strict mode, this method will only rely on the C<XDG_RUNTIME_DIR> to find this directory.
452             Under non-strict mode, system specific methods may be used, if the environment variable is not
453             set:
454              
455             =over 4
456              
457             =item Linux systemd
458              
459             The path C</run/user/UID> will be used, if it exists, and fulfills the requirements of the spec.
460              
461             =back
462              
463             =head2 data_dirs
464              
465             my $dirs = $xdg->data_dirs;
466              
467             Returns the system data directories, not modified for the application. Per the
468             specification, the returned string is C<:>-delimited, except on Windows where it
469             is C<;>-delimited.
470              
471             For portability L</data_dirs_list> is preferred.
472              
473             =head2 data_dirs_list
474              
475             [version 0.06]
476              
477             my @dirs = $xdg->data_dirs_list;
478              
479             Returns the system data directories as a list of path class objects.
480              
481             =head2 config_dirs
482              
483             my $dirs = $xdg->config_dirs;
484              
485             Returns the system config directories, not modified for the application. Per
486             the specification, the returned string is :-delimited, except on Windows where it
487             is C<;>-delimited.
488              
489             For portability L</config_dirs_list> is preferred.
490              
491             =head2 config_dirs_list
492              
493             [version 0.06]
494              
495             my @dirs = $xdg->config_dirs_list;
496              
497             Returns the system config directories as a list of path class objects.
498              
499             =head2 exe_dir
500              
501             [version 0.10]
502              
503             my $exe = $xdg->exe_dir;
504              
505             Returns the user-specific executable files directory C<$HOME/.local/bin>, if it exists. If it
506             does not exist then C<undef> will be returned. This directory I<should> be added to the C<PATH>
507             according to the spec.
508              
509             =head2 lookup_data_file
510              
511             my $xdg = File::XDG->new( name => $name, api => 1 ); # recommended
512             my $path = $xdg->lookup_data_File($filename);
513              
514             Looks up the data file by searching for C<./$name/$filename> (where C<$name> is
515             provided by the constructor) relative to all base directories indicated by
516             C<$XDG_DATA_HOME> and C<$XDG_DATA_DIRS>. If an environment variable is either
517             not set or empty, its default value as defined by the specification is used
518             instead. Returns a path class object.
519              
520             my $xdg = File::XDG->new( name => $name ); # back compat only
521             my $path = $xdg->lookup_data_file($subdir, $filename);
522              
523             Looks up the data file by searching for C<./$subdir/$filename> relative to all base
524             directories indicated by C<$XDG_DATA_HOME> and C<$XDG_DATA_DIRS>. If an environment
525             variable is either not set or empty, its default value as defined by the
526             specification is used instead. Returns a path class object.
527              
528             =head2 lookup_config_file
529              
530             my $xdg = File::XDG->new( name => $name, api => 1 ); # recommended
531             my $path = $xdg->lookup_config_file($filename);
532              
533             Looks up the configuration file by searching for C<./$name/$filename> (where C<$name> is
534             provided by the constructor) relative to all base directories indicated by
535             C<$XDG_CONFIG_HOME> and C<$XDG_CONFIG_DIRS>. If an environment variable is
536             either not set or empty, its default value as defined by the specification
537             is used instead. Returns a path class object.
538              
539             my $xdg = File::XDG->new( name => $name ); # back compat only
540             my $path = $xdg->lookup_config_file($subdir, $filename);
541              
542             Looks up the configuration file by searching for C<./$subdir/$filename> relative to
543             all base directories indicated by C<$XDG_CONFIG_HOME> and C<$XDG_CONFIG_DIRS>. If an
544             environment variable is either not set or empty, its default value as defined
545             by the specification is used instead. Returns a path class object.
546              
547             =head1 SEE ALSO
548              
549             L<XDG Base Directory specification, version 0.7|http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>
550              
551             =head1 CAVEATS
552              
553             This module intentionally and out of necessity does not follow the spec on the following platforms:
554              
555             =over 4
556              
557             =item C<MSWin32> (Strawberry Perl, Visual C++ Perl, etc)
558              
559             The spec requires C<:> as the path separator, but use of this character is essential for absolute path names in
560             Windows, so the Windows Path separator C<;> is used instead.
561              
562             There are no global data or config directories in windows so the data and config directories are empty list instead of
563             the default UNIX locations.
564              
565             The base directory instead of being the user's home directory is C<%LOCALAPPDATA%>. Arguably the data and config
566             base directory should be C<%APPDATA%>, but cache should definitely be in C<%LOCALAPPDATA%>, and we chose to use just one
567             base directory for simplicity.
568              
569             =back
570              
571             =head1 SEE ALSO
572              
573             =over 4
574              
575             =item L<Path::Class>
576              
577             Portable native path class used by this module used by default (api = 0) and optionally (api = 1).
578              
579             =item L<Path::Tiny>
580              
581             Smaller lighter weight path class used optionally (api = 0) and by default (api = 1).
582              
583             =item L<Path::Spec>
584              
585             Core Perl library for working with file and directory paths.
586              
587             =item L<File::BaseDir>
588              
589             Provides similar functionality to this module with a different interface.
590              
591             =back
592              
593             =head1 AUTHOR
594              
595             Original author: Síle Ekaterin Aman
596              
597             Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt>
598              
599             =head1 COPYRIGHT AND LICENSE
600              
601             This software is copyright (c) 2012-2022 by Síle Ekaterin Aman.
602              
603             This is free software; you can redistribute it and/or modify it under
604             the same terms as the Perl 5 programming language system itself.
605              
606             =cut