• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env perl
2
3use strict;
4use warnings;
5use Getopt::Long();
6use Pod::Usage();
7
8my $curl = 'curl';
9my $shell = 'zsh';
10my $help = 0;
11Getopt::Long::GetOptions(
12    'curl=s' => \$curl,
13    'shell=s' => \$shell,
14    'help' => \$help,
15) or Pod::Usage::pod2usage();
16Pod::Usage::pod2usage() if $help;
17
18my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)';
19my @opts = parse_main_opts('--help', $regex);
20
21if ($shell eq 'fish') {
22    print "# curl fish completion\n\n";
23    print qq{$_ \n} foreach (@opts);
24} elsif ($shell eq 'zsh') {
25    my $opts_str;
26
27    $opts_str .= qq{  $_ \\\n} foreach (@opts);
28    chomp $opts_str;
29
30my $tmpl = <<"EOS";
31#compdef curl
32
33# curl zsh completion
34
35local curcontext="\$curcontext" state state_descr line
36typeset -A opt_args
37
38local rc=1
39
40_arguments -C -S \\
41$opts_str
42  '*:URL:_urls' && rc=0
43
44return rc
45EOS
46
47    print $tmpl;
48} else {
49    die("Unsupported shell: $shell");
50}
51
52sub parse_main_opts {
53    my ($cmd, $regex) = @_;
54
55    my @list;
56    my @lines = call_curl($cmd);
57
58    foreach my $line (@lines) {
59        my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next;
60
61        my $option = '';
62
63        $arg =~ s/\:/\\\:/g if defined $arg;
64
65        $desc =~ s/'/'\\''/g if defined $desc;
66        $desc =~ s/\[/\\\[/g if defined $desc;
67        $desc =~ s/\]/\\\]/g if defined $desc;
68        $desc =~ s/\:/\\\:/g if defined $desc;
69
70        if ($shell eq 'fish') {
71            $option .= "complete --command curl";
72            $option .= " --short-option '" . strip_dash(trim($short)) . "'"
73                if defined $short;
74            $option .= " --long-option '" . strip_dash(trim($long)) . "'"
75                if defined $long;
76            $option .= " --description '" . strip_dash(trim($desc)) . "'"
77                if defined $desc;
78        } elsif ($shell eq 'zsh') {
79            $option .= '{' . trim($short) . ',' if defined $short;
80            $option .= trim($long)  if defined $long;
81            $option .= '}' if defined $short;
82            $option .= '\'[' . trim($desc) . ']\'' if defined $desc;
83
84            $option .= ":'$arg'" if defined $arg;
85
86            $option .= ':_files'
87                if defined $arg and ($arg eq '<file>' || $arg eq '<filename>'
88                    || $arg eq '<dir>');
89        }
90
91        push @list, $option;
92    }
93
94    # Sort longest first, because zsh won't complete an option listed
95    # after one that's a prefix of it.
96    @list = sort {
97        $a =~ /([^=]*)/; my $ma = $1;
98        $b =~ /([^=]*)/; my $mb = $1;
99
100        length($mb) <=> length($ma)
101    } @list if $shell eq 'zsh';
102
103    return @list;
104}
105
106sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
107sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s };
108
109sub call_curl {
110    my ($cmd) = @_;
111    my $output = `"$curl" $cmd`;
112    if ($? == -1) {
113        die "Could not run curl: $!";
114    } elsif ((my $exit_code = $? >> 8) != 0) {
115        die "curl returned $exit_code with output:\n$output";
116    }
117    return split /\n/, $output;
118}
119
120__END__
121
122=head1 NAME
123
124completion.pl - Generates tab-completion files for various shells
125
126=head1 SYNOPSIS
127
128completion.pl [options...]
129
130    --curl   path to curl executable
131    --shell  zsh/fish
132    --help   prints this help
133
134=cut
135