]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Fix autocompletion a bit more, add tests
authorDavid Woodhouse <dwmw2@infradead.org>
Wed, 8 Apr 2020 14:15:00 +0000 (15:15 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Wed, 8 Apr 2020 14:15:00 +0000 (15:15 +0100)
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
bash/openconnect.bash
main.c
tests/Makefile.am
tests/autocompletion [new file with mode: 0755]

index 89d55ee75e4ec790c9ea113c068d4fdbca193669..2424fe41307ec2d39bdf401133d5a1aa56aef795 100644 (file)
@@ -76,7 +76,7 @@ _complete_openconnect () {
     export COMP_LINE COMP_POINT COMP_CWORD COMP_KEY COMP_TYPE
     COMP_WORDS[0]="--autocomplete"
     local IFS=$'\n'
-    COMPREPLY=( $(/home/dwmw/git/openconnect/gtls-ibm/openconnect "${COMP_WORDS[@]}") )
+    COMPREPLY=( $(openconnect "${COMP_WORDS[@]}") )
     case "${COMPREPLY[0]}" in
        FILENAME)
            if [ "${COMPREPLY[1]}" != "" ]; then
diff --git a/main.c b/main.c
index 366c5770807772548a6f5f46a8b43f50be735456..20744397f015c692558c0664c53d4efb0c3dad7c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1139,14 +1139,24 @@ static int autocomplete(int argc, char **argv)
 
        opterr = 0;
 
-       while (1) {
-               int match_opt = (argv[optind] == comp_opt);
+       while (argv[optind]) {
+               int thisind = optind;
 
                /* Don't let getopt_long() assume it's a separator; instead
                 * assume they want to tab-complete to a real long option. */
-               if (match_opt && !strcmp(comp_opt, "--"))
+               if (argv[thisind] == comp_opt)
                        goto empty_opt;
 
+               /* Skip over non-option elements, in an attempt to prevent
+                * getopt_long() from reordering the array as we go. The problem
+                * is that we've seen it *delay* the reordering. So it processes
+                * the argv element *after* the non-option, but argv[optind] is
+                * still pointing to the non-option. */
+               if (argv[thisind][0] != '-') {
+                       optind++;
+                       continue;
+               }
+
                opt = getopt_long(argc, argv,
 #ifdef _WIN32
                                  "C:c:Dde:F:g:hi:k:m:P:p:Q:qs:u:Vvx:",
@@ -1158,23 +1168,41 @@ static int autocomplete(int argc, char **argv)
                if (opt == -1)
                        break;
 
-               if (match_opt) {
+               if (argv[thisind] == comp_opt) {
+                       char *matcher = NULL;
                empty_opt:
-                       /* No autocompletion for short options */
-                       if (!strncmp(comp_opt, "--", 2)) {
-                               int complen = strlen(comp_opt + 2);
+                       if (!strncmp(comp_opt, "--", 2))
+                               matcher = comp_opt + 2;
+                       else if (!strcmp(comp_opt, "-"))
+                               matcher = comp_opt + 1;
+
+                       if (matcher) {
+                               int matchlen = strlen(matcher);
                                const struct option *p = long_options;
 
                                while (p->name) {
-                                       if (!strncmp(comp_opt + 2, p->name, complen))
+                                       if (!strncmp(matcher, p->name, matchlen))
                                                printf("--%s\n", p->name);
                                        p++;
                                }
-                       }
+                       } else if (comp_opt[0] == '-') {
+                               if (comp_opt[1] && !comp_opt[2]) {
+                                       /* Single-char -X option. */
+                                       const struct option *longopt = long_options;
+                                       while (longopt->name) {
+                                               if (comp_opt[1] == longopt->val) {
+                                                       printf("--%s\n", longopt->name);
+                                                       break;
+                                               }
+                                               longopt++;
+                                       }
+                               }
+                       } else
+                               printf("HOSTNAME\n");
+
                        return 0;
                }
 
-
                if (optarg == comp_opt) {
                        switch (opt) {
                        case 'k': /* --sslkey */
index 14f51c5a224117f3d05e96b0f9e0acce048498f5..bcc91212f625f3859c8117417f9e0604f7d42fca 100644 (file)
@@ -45,7 +45,7 @@ EXTRA_DIST = certs/ca.pem certs/ca-key.pem certs/user-cert.pem $(USER_KEYS) $(US
        configs/user-cert.prm softhsm2.conf.in softhsm ns.sh configs/test-dtls-psk.config \
        scripts/vpnc-script scripts/vpnc-script-detect-disconnect
 
-dist_check_SCRIPTS =
+dist_check_SCRIPTS = autocompletion
 
 if HAVE_NETNS
 dist_check_SCRIPTS += dtls-psk sigterm
diff --git a/tests/autocompletion b/tests/autocompletion
new file mode 100755 (executable)
index 0000000..d2f0ae6
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# Bash completion for OpenConnect
+#
+# Copyright © David Woodhouse <dwmw2@infradead.org>
+#
+# Author: David Woodhouse <dwmw2@infradead.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# version 2.1, as published by the Free Software Foundation.
+#
+# 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
+# Lesser General Public License for more details.
+
+
+# Consider a command line like the following:
+#
+# openconnect -c --authenticate\ -k -k "'"'"'.pem --authgroup 'foo
+# bar' --o\s linux-64 myserver
+
+
+OPENCONNECT="${OPENCONNECT:-${top_builddir}/openconnect}"
+
+do_test()
+{
+       if [ "$(COMP_CWORD=$WORD $OPENCONNECT --autocomplete "$@")" != "$RESULT" ]; then
+          echo "Autocomplete failed (word $WORD) for '$@'"
+          exit 1
+       fi
+       echo "Autocomplete OK (word $WORD) for '$@'"
+}
+
+WORD=1
+RESULT="HOSTNAME"
+do_test ""
+
+WORD=1
+RESULT="--certificate"
+do_test -c
+
+WORD=3
+RESULT="FILENAME
+!*.@(pem|der|p12|crt)"
+do_test foo -k ''
+
+WORD=2
+RESULT="--protocol"
+do_test somehost --proto
+
+WORD=1
+RESULT="HOSTNAME"
+do_test somehost --proto
+
+WORD=3
+RESULT="FILENAME
+!*.@(pem|der|crt)"
+do_test foo --cafile ''
+
+WORD=4
+RESULT="none
+off
+all
+stateless"
+do_test -k foo --compr ''
+