2016-05-19 19:23:00 +08:00
# ip(8) completion for fish
# The difficulty here is that ip allows abbreviating options, so we need to complete "ip a" like "ip address", but not "ip m" like "ip mroute"
# Also the manpage and even the grammar it accepts is utter shite (options can only be before commands, some things are only in the BNF, others only in the text)
# It also quite likes the word "dev", even though it needs it less than the BNF specifies
set -l ip_commands link address addrlabel route rule neigh ntable tunnel tuntap maddr mroute mrule monitor xfrm netns l2tp tcp_metrics
set -l ip_addr a ad add addr addre addres address
set -l ip_link l li lin link
set -l ip_all_commands $ip_commands $ip_addr $ip_link
function __fish_ip_commandwords
2018-11-12 23:28:24 +08:00
set -l skip 0
set -l cmd ( commandline -opc )
# HACK: Handle and/or/not specially because they have hardcoded completion behavior
# that doesn't remove them from the commandline
if contains -- $cmd [ 1 ] and or not
set -e cmd [ 1 ]
end
# Remove the first word because it's "ip" or an alias for it
set -e cmd [ 1 ]
set -l have_command 0
for word in $cmd
switch $word
# Normalize the commands to their full form - `ip a` is `ip address`
# This can't be just an unambiguity check because there's also `ip addrlabel`
case a ad add addr addre addres address
# "addr add" is a thing, so we can only echo "address" if it's actually the command
if test $have_command = 0
set have_command 1
echo address
else
echo $word
end
case l li lin link
if test $have_command = 0
set have_command 1
echo link
else
echo $word
end
case "addrl*"
if test $have_command = 0
set have_command 1
echo addrlabel
else
echo $word
end
case r ro rou rout route
if test $have_command = 0
set have_command 1
echo route
else
echo $word
end
case "ru*"
if test $have_command = 0
set have_command 1
echo rule
else
echo $word
end
case n ne nei neig neigh
if test $have_command = 0
set have_command 1
echo neigh
else
echo $word
end
case nt
if test $have_command = 0
set have_command 1
echo ntable
else
echo $word
end
case t tu tun tunn tunne tunnel
if test $have_command = 0
set have_command 1
echo tunnel
else
echo $word
end
case "tunt*"
if test $have_command = 0
set have_command 1
echo tuntap
else
echo $word
end
case m ma mad madd maddr maddre maddres maddress
if test $have_command = 0
set have_command 1
echo maddress
else
echo $word
end
case mr "mro*"
if test $have_command = 0
set have_command 1
echo mroute
else
echo $word
end
case "mru*"
if test $have_command = 0
set have_command 1
echo mrule
else
echo $word
end
case "mo*"
if test $have_command = 0
set have_command 1
echo monitor
else
echo $word
end
case "x*"
if test $have_command = 0
set have_command 1
echo xfrm
else
echo $word
end
case "net*"
if test $have_command = 0
set have_command 1
echo netns
else
echo $word
end
case "l*"
if test $have_command = 0
set have_command 1
echo l2tp
else
echo $word
end
case "tc*"
if test $have_command = 0
set have_command 1
echo tcp_metrics
else
echo $word
end
case "to*"
if test $have_command = 0
set have_command 1
echo token
else
echo $word
end
case '-n' '-netns' '--netns'
if test $have_command = 0
set skip 1
else
echo $word
end
case '-*'
test $have_command = 0
and continue
echo $word
case '*'
if test $skip = 1
set skip 0
continue
end
echo $word
end
end
# Print an empty line if the current token is empty, so we know that the one before it is finished
# TODO: For some reason it is necessary to always print the current token - why doesn't the above loop catch it?
set -l token ( commandline -ct )
echo $token
2016-05-19 19:23:00 +08:00
end
function __fish_ip_device
2018-11-12 23:28:24 +08:00
ip -o link show | while read a b c
printf '%s\t%s\n' ( string replace ':' '' -- $b ) "Device"
end
2016-05-19 19:23:00 +08:00
end
function __fish_ip_scope
2018-11-12 23:28:24 +08:00
if test -r /etc/iproute2/rt_scopes
string replace -r '#.*' '' < /etc/iproute2/rt_scopes \
| string match -v '^\s*$' \
| string replace -r '(\S+)\s*(\S+)' '$1\t$2\n$2\t$1' \
| string match -rv '^(global|link|host).*' # Ignore scopes with better descriptions
end
# Predefined scopes
printf '%s\t%s\n' global "Address is globally valid" \
link "Address is link-local, only valid on this device" \
host "Address is only valid on this host"
2016-05-19 19:23:00 +08:00
end
2018-11-12 23:22:15 +08:00
function __fish_ip_types
2018-11-12 23:28:24 +08:00
printf '%s\t%s\n' \
macvtap "Virtual interface based on link layer address (MAC) and TAP." \
vcan "Virtual Controller Area Network interface" \
veth "Virtual ethernet interface" \
vlan "802.1q tagged virtual LAN interface" \
vxlan "Virtual eXtended LAN" \
ip6tnl "Virtual tunnel interface IPv4|IPv6 over IPv6" \
ipip "Virtual tunnel interface IPv4 over IPv4" \
sit "Virtual tunnel interface IPv6 over IPv4" \
gre "Virtual tunnel interface GRE over IPv4" \
gretap "Virtual L2 tunnel interface GRE over IPv4" \
ip6gre "Virtual tunnel interface GRE over IPv6" \
ip6gretap "Virtual L2 tunnel interface GRE over IPv6" \
vti "Virtual tunnel interface" \
nlmon "Netlink monitoring device" \
ipvlan "Interface for L3 (IPv6/IPv4) based VLANs" \
lowpan "Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth" \
geneve "GEneric NEtwork Virtualization Encapsulation"
2018-11-12 23:22:15 +08:00
end
2016-05-19 19:23:00 +08:00
function __fish_complete_ip
2018-11-12 23:28:24 +08:00
set -l cmd ( __fish_ip_commandwords )
set -l count ( count $cmd )
switch " $cmd [1] "
case address
# We're still _on_ the second word, which is the subcommand
if not set -q cmd [ 3 ]
printf '%s\t%s\n' add "Add new protocol address" \
delete "Delete protocol address" \
show "Look at protocol addresses" \
flush "Flush protocol addresses"
else
switch $cmd [ 2 ]
# Change and replace are undocumented (apart from mentions in the BNF)
case add change replace
switch $count
case 3
# __fish_ip_complete_ip
case '*'
switch $cmd [ - 2 ]
case dev
__fish_ip_device
case scope
__fish_ip_scope
# TODO: Figure out how to complete these
case label
# Prefix
case local peer broadcast
# Address
case valid_lft preferred_lft
# Lifetime
case '*'
printf '%s\t%s\n' forever "Keep address valid forever" \
2019-11-25 19:03:25 +08:00
home "(Ipv6 only) Designate address as home address" \
2018-11-12 23:28:24 +08:00
nodad "(Ipv6 only) Don't perform duplicate address detection" \
dev "Add address to specified device" \
scope "Set scope of address" \
label "Tag address with label"
end
end
case delete
switch $count
case 3
ip -o addr show | while read a b c d e
echo $d
end
case 4
# A dev argument is mandatory, but contrary to the BNF, other things (like "scope") are also valid here
# And yes, unlike e.g. show, this _needs_ the "dev" before the device
# Otherwise it barfs and says "??? prefix is expected"
# Anyway, try to steer the user towards supplying a device
echo dev
case 5
switch $cmd [ - 2 ]
case dev
ip -o addr show | string match " * $cmd [3]* " | while read a b c
echo $b
end
# TODO: Moar
end
case show save flush # These take the same args
switch $cmd [ - 2 ]
case dev
__fish_ip_device
case scope
__fish_ip_scope
case to
# Prefix
case label
# Label-pattern
case '*'
printf '%s\t%s\n' up "Only active devices" \
dev "Limit to a certain device" \
scope "Limit scope" \
to "Limit prefix" \
label "Limit by label" \
dynamic "(Ipv6 only) Limit to dynamic addresses" \
permanent "(Ipv6 only) Limit to permanent addresses"
__fish_ip_device
# TODO: Moar
end
end
end
end
case link
if not set -q cmd [ 3 ]
printf '%s\t%s\n' add "Add virtual link" \
delete "Delete virtual link" \
set "Change device attributes" \
show "Display device attributes" \
help "Display help"
else
# TODO: Add moar
switch $cmd [ 2 ]
case add
switch $cmd [ - 2 ]
2018-11-12 23:22:15 +08:00
case link
2018-11-12 23:28:24 +08:00
__fish_ip_device
case name # freeform, uncompleteable.
case type
2018-11-12 23:22:15 +08:00
__fish_ip_types
case txqueuelen
case address
case broadcast
case mtu index numtxqueues numrxqueues gso_max_size gso_max_segs
# These all take some kind of number?
seq 0 50
case add '*'
# We assume if we haven't checked it above, we're back to `ip link add`, with any optionals completed.
# So we now suggest optionals.
printf '%s\t%s\n' \
2018-11-12 23:28:24 +08:00
link "" \
type "" \
txqueuelen PACKETS \
address LLADDR \
broadcast LLADDR \
mtu MTU \
index IDX \
numtxqueues QUEUE_COUNT \
numrxqueues QUEUE_COUNT \
gso_max_size BYTES \
gso_max_segs
end
case delete
case set
switch $cmd [ - 2 ]
2018-11-12 23:22:15 +08:00
case type
__fish_ip_types
echo bridge_slave
echo bond_slave
case arp dynamic { all,} multicast pro{ misc,todown} trailers spoofchk query_rss trust
echo on
echo off
case name alias
case address broadcast
case mtu txqueuelen
case netns
case link-netnsid
case vf mac
case { { max,min} _tx_,} rate
case node_guid port_guid
case state
echo auto
echo enable
echo disable
case master dev # Yes, the word "dev" is allowed here!
__fish_ip_device
case nomaster
case vrf
case xdp'*'
case object section pinned
case addrgenmode
case macaddr
case group
case set '*'
__fish_ip_device
echo group
echo up
echo down
end
2018-11-12 23:28:24 +08:00
case show
case help
end
end
end
2016-05-19 19:23:00 +08:00
end
2017-01-16 06:51:10 +08:00
2016-05-19 19:23:00 +08:00
complete -f -c ip
complete -f -c ip -a '(__fish_complete_ip)'
complete -f -c ip -n " not __fish_seen_subcommand_from $ip_all_commands " -a " $ip_commands "
# Yes, ip only takes options before "objects"
complete -c ip -s b -l batch -d "Read commands from file or stdin" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -l force -d "Don't terminate on errors in batch mode" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -s V -l Version -d "Print the version" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s s -l stats -d "Output more information" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s d -l details -d "Output more detailed information" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s l -l loops -d "Specify maximum number of loops for 'ip addr flush'" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s f -l family -d "The protocol family to use" -a "inet inet6 bridge ipx dnet link any" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s 4 -d "Short for --family inet" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s 6 -d "Short for --family inet6" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s B -d "Short for --family bridge" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s D -d "Short for --family decnet" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s I -d "Short for --family ipx" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s O -d "Short for --family link" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s o -l oneline -d "Output on one line" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s r -l resolve -d "Resolve names and print them instead of addresses" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s n -l net -l netns -d "Use specified network namespace" -n " not __fish_seen_subcommand_from $ip_commands "
complete -c ip -f -s a -l all -d "Execute command for all objects" -n " not __fish_seen_subcommand_from $ip_commands "