mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-25 18:03:37 +08:00
b5e746cbd4
When adding a VLAN-enabled interface, it is named like enp0s31f6.100@enp0s31f6 with the physical interface being appended behind an @. But subsequent ip commands operate on the interface name without this suffix, so it needs to be removed when completing interface names in __fish_ip_device
473 lines
21 KiB
Fish
473 lines
21 KiB
Fish
# 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
|
|
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
|
|
end
|
|
|
|
function __fish_ip_device
|
|
command ip -o link show | while read -l a b c
|
|
printf '%s\t%s\n' (string replace -r '(@.*)?:' '' -- $b) Device
|
|
end
|
|
end
|
|
|
|
function __fish_ip_scope
|
|
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"
|
|
end
|
|
|
|
function __fish_ip_netns_list
|
|
command ip netns list | while read -l a b c
|
|
echo -- $a
|
|
end
|
|
end
|
|
|
|
function __fish_ip_types
|
|
printf '%s\t%s\n' \
|
|
bridge "Ethernet Bridge device" \
|
|
bond "Bonding device" \
|
|
dummy "Dummy network interface" \
|
|
hsr "High-availability Seamless Redundancy device" \
|
|
ifb "Intermediate Functional Block device" \
|
|
ipoib "IP over Infiniband device" \
|
|
macvlan "Virtual interface base on link layer address (MAC)" \
|
|
macvtap "Virtual interface based on link layer address (MAC) and TAP." \
|
|
vcan "Virtual Controller Area Network interface" \
|
|
vxcan "Virtual Controller Area Network tunnel 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" \
|
|
erspan "Encapsulated Remote SPAN over GRE and IPv4" \
|
|
ip6gre "Virtual tunnel interface GRE over IPv6" \
|
|
ip6gretap "Virtual L2 tunnel interface GRE over IPv6" \
|
|
ip6erspan "Encapsulated Remote SPAN over GRE and IPv6" \
|
|
vti "Virtual tunnel interface" \
|
|
nlmon "Netlink monitoring device" \
|
|
ipvlan "Interface for L3 (IPv6/IPv4) based VLANs" \
|
|
ipvtap "Interface for L3 (IPv6/IPv4) based VLANs and TAP" \
|
|
lowpan "Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth" \
|
|
geneve "GEneric NEtwork Virtualization Encapsulation" \
|
|
bareudp "Bare UDP L3 encapsulation support" \
|
|
macsec "Interface for IEEE 802.1AE MAC Security (MACsec)" \
|
|
vrf "Interface for L3 VRF domains" \
|
|
netdevsim "Interface for netdev API tests" \
|
|
rmnet "Qualcomm rmnet device" \
|
|
xfrm "Virtual xfrm interface"
|
|
end
|
|
|
|
function __fish_complete_ip
|
|
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" \
|
|
home "(Ipv6 only) Designate address as home address" \
|
|
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
|
|
command ip -o addr show | while read -l 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
|
|
command ip -o addr show | string match "*$cmd[3]*" | while read -l a b c
|
|
echo $b
|
|
end
|
|
# TODO: Moar
|
|
end
|
|
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
|
|
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]
|
|
case link
|
|
__fish_ip_device
|
|
case name # freeform, uncompleteable.
|
|
case type
|
|
__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' \
|
|
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]
|
|
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
|
|
case show
|
|
case help
|
|
end
|
|
end
|
|
case netns
|
|
if not set -q cmd[3]
|
|
printf '%s\t%s\n' add "Add network namespace" \
|
|
attach "Attach process to network namespace" \
|
|
delete "Delete network namespace" \
|
|
set "Change network namespace attributes" \
|
|
identify "Display network namespace for a process id" \
|
|
pids "Display process ids of processes running in network namespace" \
|
|
monitor "Report as network namespace names are added and deleted" \
|
|
exec "Execute command in network namespace" \
|
|
help "Display help" \
|
|
list "List network namespaces"
|
|
else
|
|
switch $cmd[2]
|
|
case delete
|
|
if not set -q cmd[4]
|
|
__fish_ip_netns_list
|
|
end
|
|
case exec
|
|
if not set -q cmd[4]
|
|
__fish_ip_netns_list
|
|
else
|
|
__fish_complete_subcommand --commandline $cmd[4..-1]
|
|
end
|
|
case pids
|
|
if not set -q cmd[4]
|
|
__fish_ip_netns_list
|
|
end
|
|
case set
|
|
if not set -q cmd[4]
|
|
__fish_ip_netns_list
|
|
end
|
|
case attach
|
|
if not set -q cmd[4]
|
|
__fish_ip_netns_list
|
|
else
|
|
__fish_complete_pids
|
|
end
|
|
case identify
|
|
if not set -q cmd[4]
|
|
__fish_complete_pids
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
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"
|