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:",
                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 */
 
--- /dev/null
+#!/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 ''
+