Linux-DesktopFiles

 view release on metacpan or  search on metacpan

Build.PL  view on Meta::CPAN

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 => {

Changes  view on Meta::CPAN

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.

Changes  view on Meta::CPAN

    - 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.

Changes  view on Meta::CPAN


    - 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

MANIFEST  view on Meta::CPAN

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

META.json  view on Meta::CPAN

{
   "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",

META.yml  view on Meta::CPAN

---
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

README.md  view on Meta::CPAN

## 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

SIGNATURE  view on Meta::CPAN

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 )