completion.pl 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #!/usr/bin/env perl
  2. #***************************************************************************
  3. # _ _ ____ _
  4. # Project ___| | | | _ \| |
  5. # / __| | | | |_) | |
  6. # | (__| |_| | _ <| |___
  7. # \___|\___/|_| \_\_____|
  8. #
  9. # Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  10. #
  11. # This software is licensed as described in the file COPYING, which
  12. # you should have received as part of this distribution. The terms
  13. # are also available at https://curl.se/docs/copyright.html.
  14. #
  15. # You may opt to use, copy, modify, merge, publish, distribute and/or sell
  16. # copies of the Software, and permit persons to whom the Software is
  17. # furnished to do so, under the terms of the COPYING file.
  18. #
  19. # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  20. # KIND, either express or implied.
  21. #
  22. ###########################################################################
  23. use strict;
  24. use warnings;
  25. use Getopt::Long();
  26. use Pod::Usage();
  27. my $curl = 'curl';
  28. my $shell = 'zsh';
  29. my $help = 0;
  30. Getopt::Long::GetOptions(
  31. 'curl=s' => \$curl,
  32. 'shell=s' => \$shell,
  33. 'help' => \$help,
  34. ) or Pod::Usage::pod2usage();
  35. Pod::Usage::pod2usage() if $help;
  36. my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)';
  37. my @opts = parse_main_opts('--help all', $regex);
  38. if ($shell eq 'fish') {
  39. print "# curl fish completion\n\n";
  40. print qq{$_ \n} foreach (@opts);
  41. } elsif ($shell eq 'zsh') {
  42. my $opts_str;
  43. $opts_str .= qq{ $_ \\\n} foreach (@opts);
  44. chomp $opts_str;
  45. my $tmpl = <<"EOS";
  46. #compdef curl
  47. # curl zsh completion
  48. local curcontext="\$curcontext" state state_descr line
  49. typeset -A opt_args
  50. local rc=1
  51. _arguments -C -S \\
  52. $opts_str
  53. '*:URL:_urls' && rc=0
  54. return rc
  55. EOS
  56. print $tmpl;
  57. } else {
  58. die("Unsupported shell: $shell");
  59. }
  60. sub parse_main_opts {
  61. my ($cmd, $regex) = @_;
  62. my @list;
  63. my @lines = call_curl($cmd);
  64. foreach my $line (@lines) {
  65. my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next;
  66. my $option = '';
  67. $arg =~ s/\:/\\\:/g if defined $arg;
  68. $desc =~ s/'/'\\''/g if defined $desc;
  69. $desc =~ s/\[/\\\[/g if defined $desc;
  70. $desc =~ s/\]/\\\]/g if defined $desc;
  71. $desc =~ s/\:/\\\:/g if defined $desc;
  72. if ($shell eq 'fish') {
  73. $option .= "complete --command curl";
  74. $option .= " --short-option '" . strip_dash(trim($short)) . "'"
  75. if defined $short;
  76. $option .= " --long-option '" . strip_dash(trim($long)) . "'"
  77. if defined $long;
  78. $option .= " --description '" . strip_dash(trim($desc)) . "'"
  79. if defined $desc;
  80. } elsif ($shell eq 'zsh') {
  81. $option .= '{' . trim($short) . ',' if defined $short;
  82. $option .= trim($long) if defined $long;
  83. $option .= '}' if defined $short;
  84. $option .= '\'[' . trim($desc) . ']\'' if defined $desc;
  85. if (defined $arg) {
  86. $option .= ":'$arg'";
  87. if ($arg =~ /<file ?(name)?>|<path>/) {
  88. $option .= ':_files';
  89. } elsif ($arg =~ /<dir>/) {
  90. $option .= ":'_path_files -/'";
  91. } elsif ($arg =~ /<url>/i) {
  92. $option .= ':_urls';
  93. } elsif ($long =~ /ftp/ && $arg =~ /<method>/) {
  94. $option .= ":'(multicwd nocwd singlecwd)'";
  95. } elsif ($arg =~ /<method>/) {
  96. $option .= ":'(DELETE GET HEAD POST PUT)'";
  97. }
  98. }
  99. }
  100. push @list, $option;
  101. }
  102. # Sort longest first, because zsh won't complete an option listed
  103. # after one that's a prefix of it.
  104. @list = sort {
  105. $a =~ /([^=]*)/; my $ma = $1;
  106. $b =~ /([^=]*)/; my $mb = $1;
  107. length($mb) <=> length($ma)
  108. } @list if $shell eq 'zsh';
  109. return @list;
  110. }
  111. sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
  112. sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s };
  113. sub call_curl {
  114. my ($cmd) = @_;
  115. my $output = `"$curl" $cmd`;
  116. if ($? == -1) {
  117. die "Could not run curl: $!";
  118. } elsif ((my $exit_code = $? >> 8) != 0) {
  119. die "curl returned $exit_code with output:\n$output";
  120. }
  121. return split /\n/, $output;
  122. }
  123. __END__
  124. =head1 NAME
  125. completion.pl - Generates tab-completion files for various shells
  126. =head1 SYNOPSIS
  127. completion.pl [options...]
  128. --curl path to curl executable
  129. --shell zsh/fish
  130. --help prints this help
  131. =cut