mirror of
https://github.com/UzixLS/picocom.git
synced 2025-07-19 07:21:18 +03:00
Added custom bash completion script for picocom
This commit is contained in:
401
bash_completion/picocom
Normal file
401
bash_completion/picocom
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
# Simple custom bash completion for picocom.
|
||||||
|
#
|
||||||
|
# Source this file like this:
|
||||||
|
# . <picocom-src-dir>/bash-completion/picocom
|
||||||
|
# Or arrange for it to be sourced by your ".bashrc",
|
||||||
|
# Or copy it in /etc/bash_completion.d (it will be sourced automatically)
|
||||||
|
#
|
||||||
|
# The idea is to provide simple custom completions for options names
|
||||||
|
# and options values, while keeping the standard ones (variable,
|
||||||
|
# pathname, etc) if the custom ones don't produce matches. It does not
|
||||||
|
# depend on the "bash-completion" package (just plain bash) and it
|
||||||
|
# does not use any of its helper functions.
|
||||||
|
#
|
||||||
|
# The code is not bullet-proof; you *can* confuse it with strange
|
||||||
|
# input if you try. There is also a known issue with giving option
|
||||||
|
# values like this:
|
||||||
|
#
|
||||||
|
# --option="value" or --option='value'
|
||||||
|
#
|
||||||
|
# That is, with an equal *and* within quotes. The solution is also
|
||||||
|
# known. For simplicty, though, I decided not to include it.
|
||||||
|
#
|
||||||
|
# See also:
|
||||||
|
# Bash mapage (man bash)
|
||||||
|
# Bash reference manual, sections 8.6, 8.7, 8.8
|
||||||
|
# The bash-completion project / package
|
||||||
|
# https://github.com/scop/bash-completion
|
||||||
|
# https://debian-administration.org/article/
|
||||||
|
# 316/An_introduction_to_bash_completion_part_1
|
||||||
|
# https://debian-administration.org/article/
|
||||||
|
# 316/An_introduction_to_bash_completion_part_2
|
||||||
|
#
|
||||||
|
# Tested with:
|
||||||
|
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
|
||||||
|
# GNU bash, version 4.4.18(1)-release (x86_64-unknown-linux-gnu)
|
||||||
|
#
|
||||||
|
# by Nick Patavalis (npat@efault.net)
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
# USA
|
||||||
|
#
|
||||||
|
|
||||||
|
# _npat_split_line
|
||||||
|
#
|
||||||
|
# Splits line into words. Line is split in almost exatcly the same way
|
||||||
|
# as readline does it. Using this function, you can specify your own
|
||||||
|
# delimiter and separator characters without having to mess with
|
||||||
|
# COMP_WORDBREAKS (which may affect other completion functions /
|
||||||
|
# scripts). In addtion, this function takes into account COMP_POINT
|
||||||
|
# and splits up to it, which may help when completing to the middle of
|
||||||
|
# an argument.
|
||||||
|
#
|
||||||
|
# Line is split taking into account quoting (backslash, single and
|
||||||
|
# double), as well as ${...} and $(...). Double-quotes, ${..} and
|
||||||
|
# $(..} can nest inside each other at any depth. Words are broken up
|
||||||
|
# either by delimiter characters (which do not appear in the resulting
|
||||||
|
# word-straem) or by separator characters, which do.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
function _npat_split_line()
|
||||||
|
{
|
||||||
|
local delimiters=$' \t\n'
|
||||||
|
local separators=$'='
|
||||||
|
local line wbreaks state word c c1 i
|
||||||
|
local -a stack
|
||||||
|
|
||||||
|
while getopts "d:s:" flag "$@"; do
|
||||||
|
case $flag in
|
||||||
|
d) delimiters=$OPTARG ;;
|
||||||
|
s) separators=$OPTARG ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# state names: ' (single quote),
|
||||||
|
# " (double quote),
|
||||||
|
# { (string-brace),
|
||||||
|
# ( (string-paren)
|
||||||
|
# ` (backquote)
|
||||||
|
# D (delimiter)
|
||||||
|
# W (naked word)
|
||||||
|
#
|
||||||
|
# ", ${, and $( can nest inside each-other any number of times
|
||||||
|
|
||||||
|
wbreaks=$delimiters$separators
|
||||||
|
line=${COMP_LINE:0:$COMP_POINT}
|
||||||
|
state=D
|
||||||
|
for (( i=0; i<${#line}; i++)); do
|
||||||
|
c=${line:i:1}
|
||||||
|
c1=${line:i+1:1}
|
||||||
|
#echo " [$i\t$c\t$c1\t$state\t$word\t\t${stack[*]} ]" > $DEBUG
|
||||||
|
if [[ $state == D || $state == W ]]; then
|
||||||
|
if [[ $c == [$wbreaks] ]]; then
|
||||||
|
if [[ $state == W ]]; then
|
||||||
|
words+=( "$word" )
|
||||||
|
word=
|
||||||
|
state=D
|
||||||
|
fi
|
||||||
|
# handling of separators vs delimiters. Separators are
|
||||||
|
# treated like delimiters, but they do appear in the
|
||||||
|
# word stream. Consequitive separators are treated as
|
||||||
|
# a single word. Ex:
|
||||||
|
#
|
||||||
|
# ab=@c=d =@= e=f
|
||||||
|
#
|
||||||
|
# will be split like this:
|
||||||
|
#
|
||||||
|
# ab | =@ | c | = | d | =@= | e | = | f
|
||||||
|
#
|
||||||
|
# assuming that = and @ are separators
|
||||||
|
if [[ $c == [$separators] ]]; then
|
||||||
|
while [[ $c == [$separators] ]]; do
|
||||||
|
word+=$c
|
||||||
|
let i++
|
||||||
|
c=${line:i:1}
|
||||||
|
done
|
||||||
|
words+=( "$word" )
|
||||||
|
word=
|
||||||
|
let i--
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
elif [[ $c == [\'\"\`] ]]; then
|
||||||
|
stack+=( W )
|
||||||
|
state=$c
|
||||||
|
elif [[ $c == '\' ]]; then
|
||||||
|
word+=$c
|
||||||
|
let i++
|
||||||
|
c=$c1
|
||||||
|
state=W
|
||||||
|
elif [[ $c == '$' && ( $c1 == '(' || $c1 == '{' ) ]]; then
|
||||||
|
word+=$c
|
||||||
|
let i++
|
||||||
|
c=$c1
|
||||||
|
stack+=( W )
|
||||||
|
state=$c1
|
||||||
|
else
|
||||||
|
state=W
|
||||||
|
fi
|
||||||
|
word+=$c
|
||||||
|
elif [[ $state == "'" ]]; then
|
||||||
|
if [[ $c == "'" ]]; then
|
||||||
|
state=${stack[-1]}
|
||||||
|
unset stack[-1]
|
||||||
|
fi
|
||||||
|
word+=$c
|
||||||
|
elif [[ $state == '"' ]]; then
|
||||||
|
if [[ $c == '\' ]]; then
|
||||||
|
word+=$c
|
||||||
|
let i++
|
||||||
|
c=$c1
|
||||||
|
elif [[ $c == '`' ]]; then
|
||||||
|
stack+=( W )
|
||||||
|
state=$c
|
||||||
|
elif [[ $c == '$' && ( $c1 == '(' || $c1 == '{' ) ]]; then
|
||||||
|
let i++
|
||||||
|
word+=$c
|
||||||
|
c=$c1
|
||||||
|
stack+=( $state )
|
||||||
|
state=$c1
|
||||||
|
elif [[ $c == '"' ]]; then
|
||||||
|
state=${stack[-1]}
|
||||||
|
unset stack[-1]
|
||||||
|
fi
|
||||||
|
word+=$c
|
||||||
|
elif [[ $state == '(' || $state == '{' || state == '`' ]]; then
|
||||||
|
if [[ $c == [\'\"] ]]; then
|
||||||
|
stack+=( $state )
|
||||||
|
state=$c
|
||||||
|
elif [[ $c == '\' ]]; then
|
||||||
|
word+=$c
|
||||||
|
let i++
|
||||||
|
c=$c1
|
||||||
|
elif [[ $c == '$' && ( $c1 == '(' || $c1 == '{' ) ]]; then
|
||||||
|
let i++
|
||||||
|
word+=$c
|
||||||
|
c=$c1
|
||||||
|
stack+=( $state )
|
||||||
|
state=$c
|
||||||
|
elif [[ $state$c == '{}' || $state$c == "()" || $state$c == '``' ]]; then
|
||||||
|
state=${stack[-1]}
|
||||||
|
unset stack[-1]
|
||||||
|
fi
|
||||||
|
word+=$c
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
words+=( "$word" )
|
||||||
|
}
|
||||||
|
|
||||||
|
_picocom_dequote()
|
||||||
|
{
|
||||||
|
local quoted="$1"
|
||||||
|
local word i inside
|
||||||
|
|
||||||
|
for (( i=0; i<${#quoted}; i++ )); do
|
||||||
|
c=${quoted:i:1}
|
||||||
|
c1=${quoted:i+1:1}
|
||||||
|
#echo " [$c] [$c1] [$inside]"
|
||||||
|
if [[ -z $inside ]]; then
|
||||||
|
if [[ $c == '\' ]]; then
|
||||||
|
let i++
|
||||||
|
c=$c1
|
||||||
|
elif [[ $c == [\'\"] ]]; then
|
||||||
|
inside=$c
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
elif [[ $inside == "'" ]]; then
|
||||||
|
if [[ $c == "'" ]]; then
|
||||||
|
inside=
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
elif [[ $inside == '"' ]]; then
|
||||||
|
if [[ $c == '\' ]]; then
|
||||||
|
let i++
|
||||||
|
c=$c1
|
||||||
|
elif [[ $c == '"' ]]; then
|
||||||
|
inside=
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
word+=$c
|
||||||
|
done
|
||||||
|
echo "$word"
|
||||||
|
}
|
||||||
|
|
||||||
|
_picocom_filter_mappings()
|
||||||
|
{
|
||||||
|
local IFS cur1 m c found
|
||||||
|
local -a cura
|
||||||
|
|
||||||
|
cur1=$(_picocom_dequote "$cur")
|
||||||
|
IFS=$', \t'
|
||||||
|
cura=( $cur1 )
|
||||||
|
IFS=$' \t\n'
|
||||||
|
for m in "${mappings[@]}"; do
|
||||||
|
found=
|
||||||
|
for c in "${cura[@]}"; do
|
||||||
|
[[ $c == "$m" ]] && { found=yes; break; }
|
||||||
|
done
|
||||||
|
[[ -z $found ]] && mapfilt+=( "$m" )
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if $1 is valid picocom option name
|
||||||
|
_picocom_is_opt()
|
||||||
|
{
|
||||||
|
local e match="$1"
|
||||||
|
for e in "${opts[@]}"; do
|
||||||
|
[[ $e == "$match" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Custom completion function for picocom
|
||||||
|
_picocom()
|
||||||
|
{
|
||||||
|
local cur cur0 cur1 prev
|
||||||
|
local -a opts baudrates mappings mapfilt
|
||||||
|
local DEBUG=/dev/pts/8
|
||||||
|
|
||||||
|
opts=( --baud --flow --databits --stopbits --parity \
|
||||||
|
--lower-rts --lower-dtr --raise-rts --raise-dtr \
|
||||||
|
--imap --omap --emap
|
||||||
|
--echo --initstring \
|
||||||
|
--noinit --noreset --hangup \
|
||||||
|
--receive-cmd --send-cmd \
|
||||||
|
--escape --no-escape \
|
||||||
|
--logfile \
|
||||||
|
--exit-after --exit \
|
||||||
|
--nolock \
|
||||||
|
--quiet --help \
|
||||||
|
)
|
||||||
|
|
||||||
|
baudrates=( 50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600 \
|
||||||
|
19200 38400 57600 115200 \
|
||||||
|
230400 460800 500000 576000 921600 1000000 1152000 1500000 \
|
||||||
|
2000000 2500000 3000000 3500000 4000000 )
|
||||||
|
|
||||||
|
mappings=( crlf crcrlf igncr lfcr lfcrlf ignlf delbs bsdel \
|
||||||
|
spchex tabhex crhex lfhex 8bithex nrmhex )
|
||||||
|
|
||||||
|
_npat_split_line
|
||||||
|
cur="${words[-1]}"
|
||||||
|
prev="${words[-2]}"
|
||||||
|
#cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
#prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
|
echo > $DEBUG
|
||||||
|
echo "------------" > $DEBUG
|
||||||
|
echo COMP_LINE "$COMP_LINE" > $DEBUG
|
||||||
|
echo COMP_POINT $COMP_POINT > $DEBUG
|
||||||
|
echo COMP_CWORD $COMP_CWORD > $DEBUG
|
||||||
|
local wrd
|
||||||
|
for wrd in "${COMP_WORDS[@]}"; do
|
||||||
|
echo -n "$wrd | " > $DEBUG
|
||||||
|
done
|
||||||
|
echo > $DEBUG
|
||||||
|
echo "$prev | $cur | " > $DEBUG
|
||||||
|
|
||||||
|
# Try to handle option values given with "="
|
||||||
|
if [[ $cur == "=" ]]; then
|
||||||
|
_picocom_is_opt "$prev" && cur=
|
||||||
|
fi
|
||||||
|
if [[ $prev == "=" && $COMP_CWORD -gt 1 ]]; then
|
||||||
|
prev1="${COMP_WORDS[COMP_CWORD-2]}"
|
||||||
|
_picocom_is_opt "$prev1" && prev="$prev1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$prev" in
|
||||||
|
-v | --receive-cmd | -s | --send-cmd)
|
||||||
|
# nothing special, just default completion
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-I | --imap | -O | --omap | -E | --emap )
|
||||||
|
[[ "$cur" =~ ^[\'\"]?[A-Za-z0-9,\ \\]*[\'\"]?$ ]] || return 0
|
||||||
|
_picocom_filter_mappings
|
||||||
|
cur1="${cur##*[, ]}"
|
||||||
|
cur0="${cur%$cur1}"
|
||||||
|
echo "$cur0 | $cur1 |" > $DEBUG
|
||||||
|
local IFS=$'\n'
|
||||||
|
COMPREPLY=( $(compgen -P "$cur0" -S "," -W "${mapfilt[*]}" -- "$cur1") )
|
||||||
|
echo "${COMPREPLY[*]}" > $DEBUG
|
||||||
|
if [[ ${#COMPREPLY[@]} -ne 0 ]]; then
|
||||||
|
compopt -o nospace
|
||||||
|
# This only works for bash-4.4 and newer
|
||||||
|
compopt -o nosort > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-e | --escape)
|
||||||
|
# nothing special, just default completion
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-f | --flow)
|
||||||
|
COMPREPLY=( $(compgen -W "hard soft none" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-b | --baud)
|
||||||
|
COMPREPLY=( $(compgen -W "${baudrates[*]}" -- "$cur") )
|
||||||
|
if [[ ${#COMPREPLY[@]} -ne 0 ]]; then
|
||||||
|
# This only works for bash 4.4 and newer
|
||||||
|
compopt -o nosort > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-y | --parity)
|
||||||
|
COMPREPLY=( $(compgen -W "even odd none" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-d | --databits)
|
||||||
|
COMPREPLY=( $(compgen -W "5 6 7 8" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-p | --stopbits)
|
||||||
|
COMPREPLY=( $(compgen -W "1 2" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-g | --logfile)
|
||||||
|
# nothing special, just default completion
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-t | --initstring)
|
||||||
|
# nothing special, just default completion
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-x | --exit-after)
|
||||||
|
# nothing special, just default completion
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ ${cur} = -* ]] ; then
|
||||||
|
COMPREPLY=( $(compgen -W "${opts[*]}" -- "$cur") )
|
||||||
|
# This only works for bash 4.4 and newer
|
||||||
|
compopt -o nosort > /dev/null 2>&1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $cur ]]; then
|
||||||
|
COMPREPLY=( $(compgen -G "/dev/tty*") )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bind custom completion function to command
|
||||||
|
complete -o default -o bashdefault -F _picocom picocom
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# mode: sh
|
||||||
|
# End:
|
70
bash_completion/readme.txt
Normal file
70
bash_completion/readme.txt
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
Starting with release 3.2, picocom includes support for custom
|
||||||
|
bash-shell completion. With this you can press the [TAB] key and have
|
||||||
|
the bash shell complete command-line option names and values and
|
||||||
|
propose valid selections for both. This makes the experience of using
|
||||||
|
picocom much more pleasant.
|
||||||
|
|
||||||
|
Custom bash-shell completion works only with recent versions of the
|
||||||
|
bash shell (>= 4.3).
|
||||||
|
|
||||||
|
To manually enable custom completion support you need to source the
|
||||||
|
file (custom completion script):
|
||||||
|
|
||||||
|
<picocom source dir>/bash_completion/picocom
|
||||||
|
|
||||||
|
Assuming you are inside the picocom source directory, you can do it
|
||||||
|
like this:
|
||||||
|
|
||||||
|
. ./bash_completion/picocom
|
||||||
|
|
||||||
|
This will enable custom completion support for the current shell
|
||||||
|
session only. Give in a ride and see if you like it.
|
||||||
|
|
||||||
|
To enable support automatically for all bash-shell sessions, you have
|
||||||
|
the following options:
|
||||||
|
|
||||||
|
1. If you are running a relatively modern Debian or Ubuntu or other
|
||||||
|
Debian-based distribution, you can simply copy the custom
|
||||||
|
completion script to the directory:
|
||||||
|
|
||||||
|
/etc/bash_completion.d/
|
||||||
|
|
||||||
|
Obviously, you need to be root to do this. Assuming you are inside
|
||||||
|
the picocom source directory, something like this will do it:
|
||||||
|
|
||||||
|
sudo cp ./bash_completion/picocom /etc/bash_completion.d/
|
||||||
|
|
||||||
|
This will enable custom completion support for picocom, globaly
|
||||||
|
(for all bash-shell users).
|
||||||
|
|
||||||
|
For other distributions and operating systems you have to check
|
||||||
|
their documentation to see if they provide a similar mechanism for
|
||||||
|
automatically sourcing custom completion scripts.
|
||||||
|
|
||||||
|
2. If you want to automatically enable support *only for the current
|
||||||
|
user*, you must arange for your user's `.bashrc` to source the
|
||||||
|
custom completion script. There are, obviously, many ways to do
|
||||||
|
this, so the following *is only a suggestion*:
|
||||||
|
|
||||||
|
Create a directory to keep the custom completion scripts
|
||||||
|
|
||||||
|
mkdir ~/.bash_completion.d
|
||||||
|
|
||||||
|
Copy the picocom completion script to the directory you
|
||||||
|
created. Assuming you are inside the picocom source directory:
|
||||||
|
|
||||||
|
cp ./bash_completion/picocom ~/.bash_completion.d
|
||||||
|
|
||||||
|
Add the following to the end of your `.bashrc`
|
||||||
|
|
||||||
|
# Source custom bash completions
|
||||||
|
if [ -d "$HOME"/.bash_completion.d ]; then
|
||||||
|
for c in "$HOME"/.bash_completion.d/*; do
|
||||||
|
[ -r "$c" ] && . "$c"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
From now on every new shell session you start will load (source)
|
||||||
|
all the custom completion scripts you have put in
|
||||||
|
`~/.bash_completion.d`
|
Reference in New Issue
Block a user