diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index d0a8e346..53a6dbad 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -8726,3 +8726,20 @@ doc/bash.1 lib/readline/doc/{rltech,hstech,rluser}.texi lib/readline/doc/{readline,history}.3 - a few small tweaks to make the language gender-neutral + + 7/18 + ---- +subst.c + - expand_word_internal: case `:' make sure the shell is either not in + posix mode or W_TILDEEXP is set before turning on W_ITILDE. The old + code would turn it on unconditionally if the W_ASSIGNMENT flag was + set. Fixes bug reported by OÄuz + + 7/20 + ---- +builtins/complete.def + - replace macros used to print compspec actions, options, and other + info with a small set of functions that traverse the structs used + to hold that information and print everything instead of eumerating + each option and action separately. From a patch submitted by + Martin Kealey diff --git a/MANIFEST b/MANIFEST index 9b601cc4..d677ddda 100644 --- a/MANIFEST +++ b/MANIFEST @@ -961,6 +961,8 @@ tests/case3.sub f tests/case4.sub f tests/casemod.tests f tests/casemod.right f +tests/complete.tests f +tests/complete.right f tests/comsub.tests f tests/comsub.right f tests/comsub1.sub f @@ -1323,6 +1325,7 @@ tests/run-braces f tests/run-builtins f tests/run-case f tests/run-casemod f +tests/run-complete f tests/run-comsub f tests/run-comsub-eof f tests/run-comsub-posix f diff --git a/builtins/complete.def b/builtins/complete.def index f33328e8..28a9ec2f 100644 --- a/builtins/complete.def +++ b/builtins/complete.def @@ -42,7 +42,7 @@ Options: command) word When completion is attempted, the actions are applied in the order the -uppercase-letter options are listed above. If multiple options are supplied, +uppercase-letter options are listed above. If multiple options are supplied, the -D option takes precedence over -E, and both take precedence over -I. Exit Status: @@ -97,11 +97,16 @@ static void print_compopts PARAMS((const char *, COMPSPEC *, int)); static void print_all_completions PARAMS((void)); static int print_cmd_completions PARAMS((WORD_LIST *)); +static void print_compoptions PARAMS((unsigned long, int)); +static void print_compactions PARAMS((unsigned long)); +static void print_arg PARAMS((const char *, const char *, int)); +static void print_cmd_name PARAMS((const char *)); + static char *Garg, *Warg, *Parg, *Sarg, *Xarg, *Farg, *Carg; static const struct _compacts { const char * const actname; - int actflag; + unsigned long actflag; int actopt; } compacts[] = { { "alias", CA_ALIAS, 'a' }, @@ -134,7 +139,7 @@ static const struct _compacts { /* This should be a STRING_INT_ALIST */ static const struct _compopt { const char * const optname; - int optflag; + unsigned long optflag; } compopts[] = { { "bashdefault", COPT_BASHDEFAULT }, { "default", COPT_DEFAULT }, @@ -485,122 +490,95 @@ remove_cmd_completions (list) return ret; } -#define SQPRINTARG(a, f) \ - do { \ - if (a) \ - { \ - x = sh_single_quote (a); \ - printf ("%s %s ", f, x); \ - free (x); \ - } \ - } while (0) +static void +print_compoptions (copts, full) + unsigned long copts; + int full; +{ + const struct _compopt *co; -#define PRINTARG(a, f) \ - do { \ - if (a) \ - printf ("%s %s ", f, a); \ - } while (0) + for (co = compopts; co->optname; co++) + if (copts & co->optflag) + printf ("-o %s ", co->optname); + else if (full) + printf ("+o %s ", co->optname); +} -#define PRINTOPT(a, f) \ - do { \ - if (acts & a) \ - printf ("%s ", f); \ - } while (0) +static void +print_compactions (acts) + unsigned long acts; +{ + const struct _compacts *ca; -#define PRINTACT(a, f) \ - do { \ - if (acts & a) \ - printf ("-A %s ", f); \ - } while (0) + /* simple flags first */ + for (ca = compacts; ca->actname; ca++) + if (ca->actopt && (acts & ca->actflag)) + printf ("-%c ", ca->actopt); -#define PRINTCOMPOPT(a, f) \ - do { \ - if (copts & a) \ - printf ("-o %s ", f); \ - } while (0) + /* then the rest of the actions */ + for (ca = compacts; ca->actname; ca++) + if (ca->actopt == 0 && (acts & ca->actflag)) + printf ("-A %s ", ca->actname); +} -#define XPRINTCOMPOPT(a, f) \ - do { \ - if (copts & a) \ - printf ("-o %s ", f); \ - else \ - printf ("+o %s ", f); \ - } while (0) +static void +print_arg (arg, flag, quote) + const char *arg, *flag; + int quote; +{ + char *x; + + if (arg) + { + x = quote ? sh_single_quote (arg) : (char *)arg; + printf ("%s %s ", flag, x); + if (x != arg) + free (x); + } +} + +static void +print_cmd_name (cmd) + const char *cmd; +{ + if (STREQ (cmd, DEFAULTCMD)) + printf ("-D"); + else if (STREQ (cmd, EMPTYCMD)) + printf ("-E"); + else if (STREQ (cmd, INITIALWORD)) + printf ("-I"); + else if (*cmd == 0) /* XXX - can this happen? */ + printf ("''"); + else + printf ("%s", cmd); +} static int print_one_completion (cmd, cs) char *cmd; COMPSPEC *cs; { - unsigned long acts, copts; - char *x; - printf ("complete "); - copts = cs->options; - - /* First, print the -o options. */ - PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault"); - PRINTCOMPOPT (COPT_DEFAULT, "default"); - PRINTCOMPOPT (COPT_DIRNAMES, "dirnames"); - PRINTCOMPOPT (COPT_FILENAMES, "filenames"); - PRINTCOMPOPT (COPT_NOQUOTE, "noquote"); - PRINTCOMPOPT (COPT_NOSORT, "nosort"); - PRINTCOMPOPT (COPT_NOSPACE, "nospace"); - PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs"); - - acts = cs->actions; - - /* simple flags next */ - PRINTOPT (CA_ALIAS, "-a"); - PRINTOPT (CA_BUILTIN, "-b"); - PRINTOPT (CA_COMMAND, "-c"); - PRINTOPT (CA_DIRECTORY, "-d"); - PRINTOPT (CA_EXPORT, "-e"); - PRINTOPT (CA_FILE, "-f"); - PRINTOPT (CA_GROUP, "-g"); - PRINTOPT (CA_JOB, "-j"); - PRINTOPT (CA_KEYWORD, "-k"); - PRINTOPT (CA_SERVICE, "-s"); - PRINTOPT (CA_USER, "-u"); - PRINTOPT (CA_VARIABLE, "-v"); - - /* now the rest of the actions */ - PRINTACT (CA_ARRAYVAR, "arrayvar"); - PRINTACT (CA_BINDING, "binding"); - PRINTACT (CA_DISABLED, "disabled"); - PRINTACT (CA_ENABLED, "enabled"); - PRINTACT (CA_FUNCTION, "function"); - PRINTACT (CA_HELPTOPIC, "helptopic"); - PRINTACT (CA_HOSTNAME, "hostname"); - PRINTACT (CA_RUNNING, "running"); - PRINTACT (CA_SETOPT, "setopt"); - PRINTACT (CA_SHOPT, "shopt"); - PRINTACT (CA_SIGNAL, "signal"); - PRINTACT (CA_STOPPED, "stopped"); + print_compoptions (cs->options, 0); + print_compactions (cs->actions); /* now the rest of the arguments */ /* arguments that require quoting */ - SQPRINTARG (cs->globpat, "-G"); - SQPRINTARG (cs->words, "-W"); - SQPRINTARG (cs->prefix, "-P"); - SQPRINTARG (cs->suffix, "-S"); - SQPRINTARG (cs->filterpat, "-X"); + print_arg (cs->globpat, "-G", 1); + print_arg (cs->words, "-W", 1); + print_arg (cs->prefix, "-P", 1); + print_arg (cs->suffix, "-S", 1); + print_arg (cs->filterpat, "-X", 1); - SQPRINTARG (cs->command, "-C"); + print_arg (cs->command, "-C", 1); /* simple arguments that don't require quoting */ - PRINTARG (cs->funcname, "-F"); + print_arg (cs->funcname, "-F", 0); - if (STREQ (cmd, DEFAULTCMD)) - printf ("-D\n"); - else if (STREQ (cmd, EMPTYCMD)) - printf ("-E\n"); - else if (STREQ (cmd, INITIALWORD)) - printf ("-I\n"); - else - printf ("%s\n", cmd); + print_cmd_name (cmd); + printf ("\n"); return (0); } @@ -611,42 +589,12 @@ print_compopts (cmd, cs, full) COMPSPEC *cs; int full; { - int copts; - printf ("compopt "); - copts = cs->options; - if (full) - { - XPRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault"); - XPRINTCOMPOPT (COPT_DEFAULT, "default"); - XPRINTCOMPOPT (COPT_DIRNAMES, "dirnames"); - XPRINTCOMPOPT (COPT_FILENAMES, "filenames"); - XPRINTCOMPOPT (COPT_NOQUOTE, "noquote"); - XPRINTCOMPOPT (COPT_NOSORT, "nosort"); - XPRINTCOMPOPT (COPT_NOSPACE, "nospace"); - XPRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs"); - } - else - { - PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault"); - PRINTCOMPOPT (COPT_DEFAULT, "default"); - PRINTCOMPOPT (COPT_DIRNAMES, "dirnames"); - PRINTCOMPOPT (COPT_FILENAMES, "filenames"); - PRINTCOMPOPT (COPT_NOQUOTE, "noquote"); - PRINTCOMPOPT (COPT_NOSORT, "nosort"); - PRINTCOMPOPT (COPT_NOSPACE, "nospace"); - PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs"); - } + print_compoptions (cs->options, full); + print_cmd_name (cmd); - if (STREQ (cmd, DEFAULTCMD)) - printf ("-D\n"); - else if (STREQ (cmd, EMPTYCMD)) - printf ("-E\n"); - else if (STREQ (cmd, INITIALWORD)) - printf ("-I\n"); - else - printf ("%s\n", cmd); + printf ("\n"); } static int diff --git a/subst.c b/subst.c index bb51a7ba..02006115 100644 --- a/subst.c +++ b/subst.c @@ -10146,20 +10146,16 @@ add_string: goto add_character; } /* If we're not in posix mode or forcing assignment-statement tilde - expansion, note where the `=' appears in the word and prepare to - do tilde expansion following the first `='. */ + expansion, note where the first `=' appears in the word and prepare + to do tilde expansion following the first `='. We have to keep + track of the first `=' (using assignoff) to avoid being confused + by an `=' in the rhs of the assignment statement. */ if ((word->flags & W_ASSIGNMENT) && (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && assignoff == -1 && sindex > 0) assignoff = sindex; if (sindex == assignoff && string[sindex+1] == '~') /* XXX */ word->flags |= W_ITILDE; -#if 0 - else if ((word->flags & W_ASSIGNMENT) && - (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && - string[sindex+1] == '~') - word->flags |= W_ITILDE; -#endif if (word->flags & W_ASSIGNARG) word->flags |= W_ASSIGNRHS; /* affects $@ */ @@ -10181,7 +10177,8 @@ add_string: goto add_character; } - if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS|W_TILDEEXP)) && + if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS)) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && string[sindex+1] == '~') word->flags |= W_ITILDE; diff --git a/tests/complete.right b/tests/complete.right new file mode 100644 index 00000000..5bc89a05 --- /dev/null +++ b/tests/complete.right @@ -0,0 +1,63 @@ +complete -f -X '!*.+(ps|PS)' gs +complete -c nice +complete -e printenv +complete -c gdb +complete -f -X '!*.texi*' texi2html +complete -j -P '%' fg +complete -g groupmod +complete -f -X '!*.dvi' dvips +complete -f -X '!*.texi*' texi2dvi +complete -v -S '=' typeset +complete -f . +complete -c nohup +complete -a unalias +complete -g groupdel +complete -A hostname telnet +complete -v -S '=' declare +complete -v -S '=' export +complete -v -S '=' local +complete -v -S '=' readonly +complete -o bashdefault -o filenames -o nospace -F _comp_cd cd +complete -f -X '!*.dvi' xdvi +complete -c type +complete -f ln +complete -f -X '!*.+(gz|tgz)' gunzip +complete -f -X '!*.texi*' makeinfo +complete -u su +complete -j -P '%' jobs +complete -o dirnames -o filenames -o nospace -d popd +complete -A signal trap +complete -o dirnames -o filenames -o nospace -d pushd +complete -f -X '!*.pdf' acroread +complete -v unset +complete -f -X '!*.+(ps|PS)' ghostview +complete -j -W '$(ps -x | tail +2 | cut -c1-5)' -P '%' wait +complete -A hostname rsh +complete -c exec +complete -f -X '!*.Z' zmore +complete -A signal kill +complete -j -P '%' disown +complete -f -X '!*.+(ps|PS)' gs +complete -f -X '!*.+(ps|PS)' gv +complete -f source +complete -c make +complete -A stopped -P '%' bg +complete -f cat +complete -d mkdir +complete -A helptopic help +complete -c eval +complete -f chown +complete -v read +complete -c -k time +complete -f -X '!*.Z' zcat +complete -f gzip +complete -W '"${GROUPS[@]}"' newgrp +complete -f -X '!*.Z' uncompress +complete -d rmdir +complete -A shopt shopt +complete -A hostname ftp +complete -f more +complete -A hostname rlogin +complete -v getopts +complete -f -X '!*.+(gz|tgz)' gzcat +./complete.tests: line 123: complete: notthere: no completion specification diff --git a/tests/complete.tests b/tests/complete.tests new file mode 100644 index 00000000..36a39833 --- /dev/null +++ b/tests/complete.tests @@ -0,0 +1,126 @@ +# Chet Ramey +# +# Copyright 2002-2020 Chester Ramey +# +# 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, or (at your option) +# any later version. +# +# TThis 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. + +complete + +# from zsh, just for testing +complete -A stopped -P '%' bg +complete -j -P '%' fg jobs disown +# this is wrong at this point +complete -j -P '%' -W '$(ps -x | tail +2 | cut -c1-5)' wait +complete -c type +complete -a unalias +complete -v getopts read unset +complete -v -S '=' declare export local readonly typeset +complete -f -- . source +complete -A shopt shopt +complete -e printenv + +complete -A helptopic help + +complete -c nohup exec nice eval +complete -c -k time + +complete -A signal trap kill + +complete -f chown ln more cat +complete -d mkdir rmdir + +complete -f -X '!*.+(gz|tgz)' gunzip gzcat zcat zmore +complete -f -X '!*.Z' uncompress zmore zcat +complete -f gzip + +complete -o dirnames -o filenames -o nospace -d pushd popd + +_comp_cd() +{ + local IFS=$' \t\n' # normalize IFS + local cur _skipdot _cdpath + local i j k + + # Tilde expansion, with side effect of expanding tilde to full pathname + case "$2" in + \~*) eval cur="$2" ;; + *) cur=$2 ;; + esac + + # no cdpath or absolute pathname -- straight directory completion + if [[ -z "${CDPATH:-}" ]] || [[ "$cur" == @(./*|../*|/*) ]]; then + # compgen prints paths one per line; could also use while loop + IFS=$'\n' + COMPREPLY=( $(compgen -d -- "$cur") ) + IFS=$' \t\n' + # CDPATH+directories in the current directory if not in CDPATH + else + IFS=$'\n' + _skipdot=false + # preprocess CDPATH to convert null directory names to . + _cdpath=${CDPATH/#:/.:} + _cdpath=${_cdpath//::/:.:} + _cdpath=${_cdpath/%:/:.} + for i in ${_cdpath//:/$'\n'}; do + if [[ $i -ef . ]]; then _skipdot=true; fi + k="${#COMPREPLY[@]}" + for j in $( compgen -d -- "$i/$cur" ); do + COMPREPLY[k++]=${j#$i/} # cut off directory + done + done + $_skipdot || COMPREPLY+=( $(compgen -d -- "$cur") ) + IFS=$' \t\n' + fi + + # variable names if appropriate shell option set and no completions + if shopt -q cdable_vars && [[ ${#COMPREPLY[@]} -eq 0 ]]; then + COMPREPLY=( $(compgen -v -- "$cur") ) + fi + + # append slash to passed directory name that is the only completion. + # readline will not do this if we complete from CDPATH + if [[ ${#COMPREPLY[@]} -eq 1 ]]; then + i=${COMPREPLY[0]} # shorthand + if [[ "$cur" == "$i" ]] && [[ "$i" != "*/" ]]; then + COMPREPLY[0]+=/ + fi + fi + return 0 +} + +complete -o filenames -o nospace -o bashdefault -F _comp_cd cd + +complete -A hostname rsh telnet rlogin ftp + +complete -u su +complete -W '"${GROUPS[@]}"' newgrp +complete -g groupdel groupmod + +complete -f -X '!*.+(ps|PS)' gs gv ghostview +complete -f -X '!*.dvi' dvips xdvi +complete -f -X '!*.pdf' acroread + +complete -f -X '!*.texi*' makeinfo texi2dvi texi2html + +complete -c gdb make + +complete -p gs +complete -p + +complete -r xdvi +complete -r notthere + +complete -r +complete diff --git a/tests/run-complete b/tests/run-complete new file mode 100644 index 00000000..f1cef84e --- /dev/null +++ b/tests/run-complete @@ -0,0 +1,2 @@ +${THIS_SH} ./complete.tests > ${BASH_TSTOUT} 2>&1 +diff ${BASH_TSTOUT} complete.right && rm -f ${BASH_TSTOUT} diff --git a/tests/tilde.right b/tests/tilde.right index 49549b40..1301c0b0 100644 --- a/tests/tilde.right +++ b/tests/tilde.right @@ -24,3 +24,5 @@ ok 1 ok 2 ok 3 ~root +foo=bar:/usr/xyz +foo=bar:~ diff --git a/tests/tilde.tests b/tests/tilde.tests index 48e1448d..374f3e6d 100644 --- a/tests/tilde.tests +++ b/tests/tilde.tests @@ -85,4 +85,8 @@ USER=root # should exist just about everywhere echo ~$USER cd "$wdir" + +echo foo=bar:~ +set -o posix; echo foo=bar:~ + exit 0