6 use POSIX
qw(strftime setlocale LC_NUMERIC LC_TIME LC_ALL);
7 use locale
qw(:numeric :time);
9 use Term
::ANSIColor
qw(:constants);
10 use constant OSC8
=> "\033]8";
11 use constant ST
=> "\033\\";
13 use AUR
::Json
qw(parse_json_aur write_json);
14 use AUR
::Query
qw(query query_multi);
15 use AUR
::Options
qw(add_from_stdin);
17 my $aur_location = $ENV{AUR_LOCATION
} // 'https://aur.archlinux.org';
20 setlocale
(LC_NUMERIC
, 'C');
21 setlocale
(LC_TIME
, 'C');
25 # Custom fixed order for info output
27 'Name' , 'PackageBase' , 'Version' , 'Description' , 'URL',
28 'Keywords' , 'License' , 'Maintainer' , 'Submitter' , 'NumVotes',
29 'Popularity' , 'OutOfDate' , 'FirstSubmitted' , 'LastModified', 'Depends',
30 'MakeDepends', 'CheckDepends', 'OptDepends'
32 my $url = join("/", $aur_location, "packages", $pkg->{'Name'});
33 say BOLD
, sprintf("%-15s", "AUR URL:"), RESET
, ' ', $url;
35 # XXX: overlap with info_expand_field() from aur-format
39 if (ref $pkg->{$k} eq 'ARRAY') {
40 $f = join(' ', @
{$pkg->{$k} // ['-']});
41 } elsif ($k eq 'LastModified' or $k eq 'OutOfDate' or $k eq 'FirstSubmitted') {
42 $f = defined $pkg->{$k} ?
gmtime $pkg->{$k} : '-';
44 $f = $pkg->{$k} // '-';
46 say BOLD
, sprintf("%-15s", "$k:"), RESET
, ' ', $f;
52 my $name = $pkg->{'Name'};
53 my $desc = $pkg->{'Description'};
54 my $ver = $pkg->{'Version'};
55 my $votes = $pkg->{'NumVotes'};
58 my $ood = defined $pkg->{'OutOfDate' }
59 ? strftime
("(Out-of-date: %d %B %Y)", gmtime $pkg->{'OutOfDate'}) : "";
60 my $orph = defined $pkg->{'Maintainer'}
62 my $pop = sprintf("%.2f", $pkg->{'Popularity'});
63 my $pre = sprintf("%s%saur/%s%s%s", BOLD
, BLUE
, RESET
, BOLD
, $name);
66 if (not exists $ENV{ANSI_COLORS_DISABLED
}) {
67 $pre = sprintf("%s;;%s%s%s%s;;%s", OSC8
, "$aur_location/packages/$name", ST
, $pre, OSC8
, ST
);
70 say $pre, ' ', GREEN
, $ver, RESET
, ' (+', $votes, ' ', $pop, '%) ',
71 $orph, BOLD
, RED
, $ood, RESET
;
72 say ' ', $desc // '-';
75 # Set union by hash value
77 my ($target, $results, $seen, $union_key) = @_;
80 %{$seen} = map { $_->{$union_key} => 1 } @
{$results};
82 push(@
{$results}, grep { !$seen->{$_->{$union_key}}++ } @
{$target});
85 # Set intersection by hash value
87 my ($target, $results, $isect_key) = @_;
88 my %seen = map { $_->{$isect_key} => 1 } @
{$target};
90 @
{$results} = grep { $seen{$_->{$isect_key}} } @
{$results};
93 # Sort a flattened array of hashes
95 my ($results, $sort_key, $reverse) = @_;
97 # Sort entries by value of specified key
98 if ($sort_key eq 'Popularity' or $sort_key eq 'NumVotes') {
99 @
{$results} = sort { $a->{$sort_key} <=> $b->{$sort_key} } @
{$results};
100 } elsif (length $sort_key) {
101 @
{$results} = sort { $a->{$sort_key} cmp $b->{$sort_key} } @
{$results};
104 @
{$results} = reverse @
{$results};
111 my $opt_multiple = 'section';
112 my $opt_type = 'search';
113 my $opt_search_by = 'name-desc';
114 my $opt_sort_key = '';
115 my $opt_color = 'auto';
119 # XXX: add option to disable set operations, --time-format
121 'a|any' => sub { $opt_multiple = 'union' },
122 'i|info' => sub { $opt_type = 'info' },
123 's|search' => sub { $opt_type = 'search' },
124 'd|desc' => sub { $opt_search_by = 'name-desc' },
125 'm|maintainer' => sub { $opt_search_by = 'maintainer' },
126 'n|name' => sub { $opt_search_by = 'name' },
127 'depends' => sub { $opt_search_by = 'depends' },
128 'makedepends' => sub { $opt_search_by = 'makedepends' },
129 'optdepends' => sub { $opt_search_by = 'optdepends' },
130 'checkdepends' => sub { $opt_search_by = 'checkdepends' },
131 'submitter' => sub { $opt_search_by = 'submitter' },
132 'provides' => sub { $opt_search_by = 'provides' },
133 'conflicts' => sub { $opt_search_by = 'conflicts' },
134 'replaces' => sub { $opt_search_by = 'replaces' },
135 'keywords' => sub { $opt_search_by = 'keywords' },
136 'groups' => sub { $opt_search_by = 'groups' },
137 'comaintainers' => sub { $opt_search_by = 'comaintainers' },
138 'q|short' => sub { $opt_format = 'short' },
139 'v|verbose' => sub { $opt_format = 'long' },
140 'J|json|raw' => sub { $opt_format = 'json' },
141 'color=s' => \
$opt_color,
142 'r|reverse' => \
$opt_reverse,
143 'k|key=s' => \
$opt_sort_key
146 # Handle '-' to take packages from stdin
147 add_from_stdin
(\
@ARGV, ['-', '/dev/stdin']);
149 if (not scalar @ARGV) {
150 say STDERR
"$argv0: at least one search term needed";
154 # Colored messages on both stdout and stderr may be desired if stdout is not
155 # connected to a terminal, e.g. when piping to less -R. (#585) When printing
156 # to a file, they should be disabled instead. Default to `--color=auto` but
157 # allow specifying other modes.
159 if (not defined $ENV{AUR_DEBUG
}) {
160 if (($opt_color eq 'auto' and -t STDOUT
) or $opt_color eq 'always') {
164 if ($colorize == 0) {
165 $ENV{ANSI_COLORS_DISABLED
} = 1;
168 # Set format depending on query type (#319)
169 if (not length($opt_format)) {
170 $opt_format = $opt_type eq 'info' ?
'long' : 'short';
173 # Retrieve JSON responses
176 # TODO: handle '-' as stdin argument
177 if ($opt_type eq 'search') {
178 # Apply union/intersection starting at the first argument
180 my %query_args = (type
=> 'search', by
=> $opt_search_by, callback
=> \
&parse_json_aur
);
181 @results = query
(term
=> $first, %query_args);
184 for my $arg (@ARGV) {
185 my @next = query
(term
=> $arg, %query_args);
187 if ($opt_multiple eq 'union') {
188 results_union
(\
@next, \
@results, \
%seen, 'Name');
190 elsif ($opt_multiple eq 'section') {
191 results_isect
(\
@next, \
@results, 'Name');
194 push(@results, @next);
198 elsif ($opt_type eq 'info') {
199 # Union/intersection do not apply to info-style requests
200 @results = query_multi
(terms
=> \
@ARGV, type
=> 'info', callback
=> \
&parse_json_aur
);
202 exit(1) if scalar @results == 0;
204 # Apply sorting criteria
205 if (length $opt_sort_key or $opt_reverse) {
206 results_rsort
(\
@results, $opt_sort_key, $opt_reverse);
208 # Format results to standard output
209 if ($opt_format eq 'short') {
210 map { format_short
($_) } @results;
212 elsif ($opt_format eq 'long') {
214 map { format_long
($_); say '' if ++$i < scalar @results } @results;
216 elsif ($opt_format eq 'json') {
217 say write_json
(\
@results);
220 die 'invalid format';
224 # vim: set et sw=4 sts=4 ft=perl: