Linux-DesktopFiles
view release on metacpan or search on metacpan
use 5.006;
use strict;
use warnings FATAL => 'all';
use Module::Build;
my $builder = Module::Build->new(
dist_name => 'Linux-DesktopFiles',
license => 'artistic_2',
dist_author => q<Daniel Èuteu <trizen@cpan.org>>,
dist_version_from => 'lib/Linux/DesktopFiles.pm',
dist_abstract => 'Fast parsing of the Linux desktop files.',
release_status => 'stable',
sign => 1,
dynamic_config => 0,
extra_manify_args => { utf8 => 1 },
configure_requires => {
'Module::Build' => 0,
},
meta_merge => {
Revision history for Perl extension Linux::DesktopFiles.
0.26 2024-10-03
- Added support for subcategories.
0.25 2017-10-22
- `parse_desktop_file()` and `parse_desktop_files()` are now context-sensitive, returning a key-value list or a HASH reference, depending on the context.
- Documentation improvements.
0.24 2017-10-21
- When `keep_unknown_categories` is true and a given entry does not belong to any category, `parse_desktop_file` will set `Categories` to [`unknown_category_key`].
0.23 2017-10-21
- Escaped strings are now unescaped.
- Added the `parse_desktop_file` method, which parses a single desktop file.
0.22 2017-10-04
- Make sure the category names are unique, so we don't store an application twice under the same category.
0.21 2017-09-21
- Fixed a minor bug for icon names that contain one or more dots (e.g.: "foo.bar.baz").
Thanks to Erik Dubois for reporting this issue (https://github.com/trizen/obmenu-generator/issues/16).
0.20 2017-09-15
- Added support for desktop IDs.
When a desktop entry is located under a local directory,
it will overwrite an existent file with the same basename.
- Removed the support for resolving absolute icon paths, as it was broken beyond repair.
0.13 2017-06-03
- Fixed a potential infinite loop for circular inherited icon directories.
0.12 2016-10-17
- Removed the no-op `keep_empty_categories` option.
- Better detection for inherited icon themes.
0.10 2016-06-04
- When a file does not have a `Name`, use the name of the file.
- Added the GitHub repository URL.
0.09 2015-01-29
- More performance improvements.
- Added the "parse" method which can parse a list of desktop files.
- Removed the "iterate_desktop_files" method. (it's faster to use the "get_desktop_files" method instead)
- Removed the support to keep empty categories after parsing.
0.08 2013-10-03
- Performance improvements.
- Added support for the 'Other' category.
- Added support for a customized terminalization format.
- Added support for any key-value substitutions.
- Added support for any key-value file filtering.
- Many other minor changes.
- Updated the documentation.
- No more unexpected dies.
0.04 2012-07-24
- Fixed the issue when requesting comments in multiple languages (ex: Comments, Comments[fr], etc...);
0.03 2012-07-24
- Skip desktop files if the "Hidden" value is set to true.
- Keep any specified keys as ->new(keys_to_keep => [...]) (previously: only alphanumeric keys)
- Improved the performance.
0.02 2012-07-21
- Improved the documentation.
0.01 2013-06-03
- original version; created by h2xs 1.23 with options
Build.PL
Changes
ignore.txt
lib/Linux/DesktopFiles.pm
LICENSE
Makefile.PL
MANIFEST This list of files
META.json
META.yml
README.md
t/file.desktop
t/Linux-DesktopFiles.t
t/pod-coverage.t
t/pod.t
SIGNATURE Added here by Module::Build
{
"abstract" : "Fast parsing of the Linux desktop files.",
"author" : [
"Daniel Èuteu <trizen@cpan.org>"
],
"dynamic_config" : 0,
"generated_by" : "Module::Build version 0.4234",
"license" : [
"artistic_2"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
---
abstract: 'Fast parsing of the Linux desktop files.'
author:
- 'Daniel Èuteu <trizen@cpan.org>'
build_requires: {}
configure_requires:
Module::Build: '0'
dynamic_config: 0
generated_by: 'Module::Build version 0.4234, CPAN::Meta::Converter version 2.150010'
license: artistic_2
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
## Linux::DesktopFiles
A fast module to get and parse the Linux .desktop files.
### INSTALLATION
To install this module type the following:
perl Build.PL
./Build
./Build test
./Build install
SHA256 48ccf80f8fae0ebb973baec9ebabb12ed948107b9c12d3534801fb3ee86c40a1 Changes
SHA256 99e7eae5f83de59f4a39c289b0dcc705abf60978c708c705898e5be9971f9c5b LICENSE
SHA256 5e29b9b8e09b5d52252b35aa6fae039e2b11fa39a839073567bd68513417fe4b MANIFEST
SHA256 6dd43889033da4f9682161158f9286a70845998975535cfbe8e5959f970bd162 META.json
SHA256 13f037f6de90ec2953a26cff4e39299ad2af993910348e44c29e1a36cc73cb27 META.yml
SHA256 8cf4f6b1320ab3822fe7f8f13928ec91d47c171c42766dc9db3ec6fcb92bf78e Makefile.PL
SHA256 b3d6a0c94aad91ed2ff60463ae4b90770d2b7a642aa313b17aac94821f6bc933 README.md
SHA256 7048e3442810dd28d6dbc4a6910644bb3ebdd348f4917c977f685b75f36dabd3 ignore.txt
SHA256 aa002f9bfed5fb93245cf88d11efca70be0251fdf2aca8d9b08ddfb200ce42a8 lib/Linux/DesktopFiles.pm
SHA256 af976f2c58d66cf155beedf1cd77f45d04cfcb9090b305db705e68f1fba7ab8f t/Linux-DesktopFiles.t
SHA256 3f8053319fe6370b66e4be1728846e26ec582ba55e101feb244c7f36482cc1ba t/file.desktop
SHA256 bc6bacebd8e3967572149a6bffe240b28aefc773efdb7c5e3c0ad2bfca86c708 t/pod-coverage.t
SHA256 69a39e15ffd8e533230052376d3b8f87f9be65e3aaf1d6124bc2bd14b3d8c467 t/pod.t
-----BEGIN PGP SIGNATURE-----
iQIzBAEBAwAdFiEEic0FAy7a5F/LlG8fvY5VX0p+GdEFAmb+ejgACgkQvY5VX0p+
GdFEYRAAgFd6jTfTw5q+aN/wslO5XAH9ORH9KJYMx/IDpx5mhXZPkXHOXAtI3mLZ
+mULXU9hdGdKa0lozWy3TmE6n9cZyTDhZ65IgnxNZapBSj0JDlnntVEKgbVaPDaV
9NeVpC14Xa22eiaSkuQc0sc8o++j91I2H0WKaSVRdAnsyZfOUnB76BiGuqXOcEvy
QelXb2VtLfiZSKfrabSXF9LfsZYsz3a+sj6+fAmh1gWzgmCT0MoHQ7R8dxYFt90Q
4j5wh2/2frjOXghjZIRuw+wUJ3rzorip6vcyeNImlvQBd7c7/OlHYVpxa7vk3dV4
lib/Linux/DesktopFiles.pm view on Meta::CPAN
package Linux::DesktopFiles;
# This module is designed to be pretty fast.
# The best uses of this module is to generate real
# time menus, based on the content of desktop files.
use 5.014;
#use strict;
#use warnings;
our $VERSION = '0.26';
our %TRUE_VALUES = (
'true' => 1,
lib/Linux/DesktopFiles.pm view on Meta::CPAN
skip_filename_re => undef,
skip_entry => undef,
substitutions => undef,
terminal => (defined($opt{terminal}) ? undef : $ENV{TERM}),
terminalize => 0,
terminalization_format => q{%s -e '%s'},
desktop_files_paths => [
qw(
/usr/local/share/applications
/usr/share/applications
)
],
keys_to_keep => [qw(Exec Name Icon)],
categories => [
qw(
lib/Linux/DesktopFiles.pm view on Meta::CPAN
if ($data{case_insensitive_cats}) {
$subcat = _make_case_insensitive($subcat);
}
@{$data{_subcategories}{$subcat}}{@subcats} = ();
}
bless \%data, $class;
}
sub get_desktop_files {
my ($self) = @_;
my %table;
foreach my $dir (@{$self->{desktop_files_paths}}) {
opendir(my $dir_h, $dir) or next;
#<<<
my $is_local = (
index($dir, '/local/') != -1
or index($dir, '/.local/') != -1
);
#>>>
foreach my $file (readdir $dir_h) {
if (substr($file, -8) eq '.desktop') {
if ($is_local or not exists($table{$file})) {
$table{$file} = "$dir/$file";
}
}
}
}
wantarray ? values(%table) : [values(%table)];
}
# Used for unescaping strings
my %Chr = (s => ' ', n => "\n", r => "\r", t => "\t", '\\' => '\\');
sub parse_desktop_file {
my ($self, $desktop_file) = @_;
# Check the filename and skip it if it matches `skip_filename_re`
if (defined $self->{skip_filename_re}) {
substr($desktop_file, rindex($desktop_file, '/') + 1) =~ /$self->{skip_filename_re}/ && return;
}
# Open and read the desktop file
sysopen my $desktop_fh, $desktop_file, 0 or return;
sysread $desktop_fh, (my $file), -s $desktop_file;
# Locate the "[Desktop Entry]" section
if ((my $index = index($file, "]\n", index($file, "[Desktop Entry]") + 15)) != -1) {
$file = substr($file, 0, $index);
}
# Parse the entry data
my %info = $file =~ /$self->{_file_keys_re}/g;
# Ignore the file when `NoDisplay` is true
if (exists $info{NoDisplay}) {
return if exists $TRUE_VALUES{$info{NoDisplay}};
}
# Ignore the file when `Hidden` is true
if (exists $info{Hidden}) {
return if exists $TRUE_VALUES{$info{Hidden}};
}
# If no 'Name' entry is defined, create one with the name of the file
$info{Name} //= substr($desktop_file, rindex($desktop_file, '/') + 1, -8);
# Unescape string escapes (\n, \t, etc.)
$info{$_} =~ s{\\(.)}{ $Chr{$1} // $1 }eg for (keys %info);
# Handle `skip_entry`
if (defined($self->{skip_entry}) and ref($self->{skip_entry}) eq 'ARRAY') {
foreach my $pair_ref (@{$self->{skip_entry}}) {
if (exists($info{$pair_ref->{key}}) and $info{$pair_ref->{key}} =~ /$pair_ref->{re}/) {
return;
}
lib/Linux/DesktopFiles.pm view on Meta::CPAN
}
# Store the icon back into `%info`
$info{Icon} = $icon;
}
wantarray ? (%info) : \%info;
}
sub parse {
my ($self, $hash_ref, @desktop_files) = @_;
foreach my $desktop_file (@desktop_files) {
my $entry = $self->parse_desktop_file($desktop_file) // next;
# Push the entry into its belonging categories
foreach my $category (@{$entry->{Categories}}) {
push @{$hash_ref->{$category}}, $entry;
}
}
$hash_ref;
}
sub parse_desktop_files {
my ($self) = @_;
my %categories;
$self->parse(\%categories, $self->get_desktop_files);
wantarray ? (%categories) : \%categories;
}
1;
__END__
=encoding utf8
=head1 NAME
Linux::DesktopFiles - Fast parsing of the Linux desktop files.
=head1 SYNOPSIS
use Linux::DesktopFiles;
my $obj = Linux::DesktopFiles->new( terminalize => 1 );
print join("\n", $obj->get_desktop_files);
my $hash_ref = $obj->parse_desktop_files;
=head1 DESCRIPTION
The C<Linux::DesktopFiles>, a very fast and simple way to parse the Linux desktop files.
=head1 CONSTRUCTOR METHODS
The following constructor methods are available:
=over 4
=item $obj = Linux::DesktopFiles->new( %options )
This method constructs a new C<Linux::DesktopFiles> object and returns it.
lib/Linux/DesktopFiles.pm view on Meta::CPAN
Linux::DesktopFiles->new(
terminal => $ENV{TERM},
terminalize => 0,
terminalization_format => "%s -e '%s'",
skip_entry => [],
skip_filename_re => [],
substitutions => [],
desktop_files_paths => ['/usr/local/share/applications',
'/usr/share/applications'],
keys_to_keep => ["Name", "Exec", "Icon"],
categories => [qw( Utility
Development
Education
Game
Graphics
AudioVideo
Network
lib/Linux/DesktopFiles.pm view on Meta::CPAN
keep_unknown_categories => 0,
unknown_category_key => 'Other',
);
=back
=head2 Main options
=over 4
=item desktop_files_paths => ['dir1', 'dir2', ...]
Sets the directories where to find the desktop files.
=item keys_to_keep => [qw(Name Exec Icon Comment ...)]
Any valid keys from the desktop files to keep in the results from C<parse_desktop_file>. The B<Categories> key is implicitly included.
=item categories => [qw(Graphics Network AudioVideo ...)]
Any valid categories from the desktop files. Any category not listed will be ignored
or stored in the B<unknown_category_key> when C<keep_unknown_categories> is set to a true value.
=back
=head2 Other options
=over 4
=item keep_unknown_categories => $bool
lib/Linux/DesktopFiles.pm view on Meta::CPAN
sprintf($self->{terminalization_format}, $self->{terminal}, $entry{Exec});
=back
=head2 Regex options
=over 4
=item skip_filename_re => qr/regex/
Skip any desktop file if its file name matches the regex.
B<NOTE:> File names are from the last slash to the end.
=item skip_entry => [{key => 'KeyName', re => qr/REGEX/i}, {...}]
Skip any desktop file if the value from a given key matches a regular expression.
The B<key> can be any valid key from the desktop files.
Example:
skip_entry => [
{key => 'Name', re => qr/(?:about|terminal)/i},
{key => 'Exec', re => qr/xterm/},
],
=item substitutions => [{key => 'KeyName', re => qr/REGEX/i, value => 'Value'}, {...}]
Substitute, by using a regex, in the returned values from desktop files.
The B<key> can be any valid key from the desktop files.
The B<re> can be any valid regular expression. Anything matched by the regex, will be
replaced with the string stored in B<value>.
For global matching/substitution, set the B<global> key to a true value.
Example:
substitutions => [
{key => 'Exec', re => qr/xterm/, value => 'tilix'},
{key => 'Exec', re => qr/\$HOME\b/, value => '/my/home', global => 1},
],
=back
=head1 SUBROUTINES/METHODS
=over 4
=item $obj->get_desktop_files()
Returns a list with the absolute paths to all desktop files from B<desktop_files_paths>.
In scalar context, returns an ARRAY reference.
=item $obj->parse(\%hash, @desktop_files)
Parse a list of desktop files into a HASH ref, where the keys of the HASH are
the categories from desktop files and the values are ARRAY references containing
information about each entry, as returned by C<parse_desktop_file()>.
=item $obj->parse_desktop_file($desktop_file)
Parse a given desktop file and return a key-value list as a result.
Example:
my %info = $obj->parse_desktop_file($desktop_file);
where C<%info> might look something like this:
my %info = (
Name => "...",
Exec => "...",
Icon => "...",
Categories => ["...", "...", "..."],
SubCategories => {Category1 => ["...", "..."], Category2 => ["...", "..."]},
);
When B<keep_unknown_categories> is true and a given entry does not belong to any category,
C<parse_desktop_file> will set B<Categories> to [C<unknown_category_key>].
Returns a HASH reference in scalar contenxt.
When a given file cannot be parsed or its specified as I<Hidden> or I<NoDisplay>, an empty list is returned (undef in scalar context).
=item $obj->parse_desktop_files()
It returns a HASH reference categorized on category names, with ARRAY references
as values, each ARRAY containing a HASH reference with the keys specified in the B<keys_to_keep>
option, and values from the desktop files.
The returned HASH reference may look something like this:
{
Utility => [ {Exec => "...", Name => "..."}, {Exec => "...", Name => "..."} ],
Network => [ {Exec => "...", Name => "..."}, {Exec => "...", Name => "..."} ],
}
This function is equivalent with:
$obj->parse(\%hash, $obj->get_desktop_files);
In list contenxt, it returns a key-value list, while, in scalar context, it returns a HASH reference.
=back
=head1 REPOSITORY
L<https://github.com/trizen/Linux-DesktopFiles>
=head1 AUTHOR
t/Linux-DesktopFiles.t view on Meta::CPAN
use warnings;
use Test::More tests => 17;
BEGIN { use_ok('Linux::DesktopFiles') }
#########################
my $obj = Linux::DesktopFiles->new(keys_to_keep => [qw(Name GenericName Comment Comment[ro] Terminal Icon Exec)],
categories => [qw(Game Archiving GTK)],);
my $t_file = 't/file.desktop';
my $desktop_file = (-f $t_file) ? $t_file : (-f $0) ? 'file.desktop' : ();
$obj->parse(\my %hash, $desktop_file);
my $info = $hash{Archiving}[0];
ok($info->{Name} eq "The right name", "Name");
ok($info->{GenericName} eq "Also this name is right", "GenericName");
ok($info->{Exec} eq "some_command -z -9", "Exec");
ok($info->{Comment} eq "This is a test!", "Comment");
ok($info->{'Comment[ro]'} eq "Acesta este un test!", "Comment[ro]");
ok($info->{Terminal} eq "true", "Terminal");
ok($info->{Icon} eq "icon_name", "Icon");
my %entry = $obj->parse_desktop_file($desktop_file);
is($entry{Name}, 'The right name', 'Name');
is($entry{Exec}, 'some_command -z -9', 'Exec');
is($entry{Terminal}, 'true', 'Terminal');
is($entry{Icon}, 'icon_name', 'Icon');
is(join(';', @{$entry{Categories}}), 'GTK;Archiving');
ok(exists($entry{SubCategories}{GTK}));
ok(exists($entry{SubCategories}{Utility}));
is(join(';', @{$entry{SubCategories}{Utility}}), 'Archiving');
t/file.desktop view on Meta::CPAN
# This is a test desktop file
[Desktop Entry]
Encoding=UTF-8
Name=The right name
GenericName=Also this name is right
Comment=This is a test!
Comment[ro]=Acesta este un test!
Type=Application
Categories=GTK;Utility;Archiving;Compression;
Terminal=true
( run in 1.449 second using v1.01-cache-2.11-cpan-299005ec8e3 )