fish: Don't eat words when completing case-insensitive paths (RHBZ#582993).
[libguestfs.git] / fish / destpaths.c
index 275db49..5ed93ec 100644 (file)
@@ -18,8 +18,6 @@
 
 #include <config.h>
 
 
 #include <config.h>
 
-#define _GNU_SOURCE            // for strndup, asprintf
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -33,6 +31,7 @@
 
 #include "fish.h"
 
 
 #include "fish.h"
 
+#ifdef HAVE_LIBREADLINE
 // From gnulib's xalloc.h:
 /* Return 1 if an array of N objects, each of size S, cannot exist due
    to size arithmetic overflow.  S must be positive and N must be
 // From gnulib's xalloc.h:
 /* Return 1 if an array of N objects, each of size S, cannot exist due
    to size arithmetic overflow.  S must be positive and N must be
@@ -48,6 +47,7 @@
    branch when S is known to be 1.  */
 # define xalloc_oversized(n, s) \
     ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
    branch when S is known to be 1.  */
 # define xalloc_oversized(n, s) \
     ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+#endif
 
 /* Readline completion for paths on the guest filesystem, also for
  * devices and LVM names.
 
 /* Readline completion for paths on the guest filesystem, also for
  * devices and LVM names.
@@ -60,6 +60,7 @@ struct word {
   int is_dir;
 };
 
   int is_dir;
 };
 
+#ifdef HAVE_LIBREADLINE
 static void
 free_words (struct word *words, size_t nr_words)
 {
 static void
 free_words (struct word *words, size_t nr_words)
 {
@@ -71,6 +72,15 @@ free_words (struct word *words, size_t nr_words)
   free (words);
 }
 
   free (words);
 }
 
+static int
+compare_words (const void *vp1, const void *vp2)
+{
+  const struct word *w1 = (const struct word *) vp1;
+  const struct word *w2 = (const struct word *) vp2;
+  return strcmp (w1->name, w2->name);
+}
+#endif
+
 char *
 complete_dest_paths_generator (const char *text, int state)
 {
 char *
 complete_dest_paths_generator (const char *text, int state)
 {
@@ -135,7 +145,7 @@ complete_dest_paths_generator (const char *text, int state)
   } while (0)
 
     /* Is it a device? */
   } while (0)
 
     /* Is it a device? */
-    if (len < 5 || strncmp (text, "/dev/", 5) == 0) {
+    if (len < 5 || STREQLEN (text, "/dev/", 5)) {
       /* Get a list of everything that can possibly begin with /dev/ */
       strs = guestfs_list_devices (g);
       APPEND_STRS_AND_FREE;
       /* Get a list of everything that can possibly begin with /dev/ */
       strs = guestfs_list_devices (g);
       APPEND_STRS_AND_FREE;
@@ -168,9 +178,9 @@ complete_dest_paths_generator (const char *text, int state)
           for (i = 0; i < dirents->len; ++i) {
             int err;
 
           for (i = 0; i < dirents->len; ++i) {
             int err;
 
-            if (strcmp (dirents->val[i].name, ".") != 0 &&
-                strcmp (dirents->val[i].name, "..") != 0) {
-              if (strcmp (dir, "/") == 0)
+            if (STRNEQ (dirents->val[i].name, ".") &&
+                STRNEQ (dirents->val[i].name, "..")) {
+              if (STREQ (dir, "/"))
                 err = asprintf (&p, "/%s", dirents->val[i].name);
               else
                 err = asprintf (&p, "%s/%s", dir, dirents->val[i].name);
                 err = asprintf (&p, "/%s", dirents->val[i].name);
               else
                 err = asprintf (&p, "%s/%s", dir, dirents->val[i].name);
@@ -210,6 +220,9 @@ complete_dest_paths_generator (const char *text, int state)
   /* This inhibits ordinary (local filename) completion. */
   rl_attempted_completion_over = 1;
 
   /* This inhibits ordinary (local filename) completion. */
   rl_attempted_completion_over = 1;
 
+  /* Sort the words so the list is stable over multiple calls. */
+  qsort (words, nr_words, sizeof (struct word), compare_words);
+
   /* Complete the string. */
   while (index < nr_words) {
     struct word *word;
   /* Complete the string. */
   while (index < nr_words) {
     struct word *word;
@@ -217,7 +230,20 @@ complete_dest_paths_generator (const char *text, int state)
     word = &words[index];
     index++;
 
     word = &words[index];
     index++;
 
-    if (strncasecmp (word->name, text, len) == 0) {
+    /* Whether we should match case insensitively here or not is
+     * determined by the value of the completion-ignore-case readline
+     * variable.  Default to case insensitive.  (See: RHBZ#582993).
+     */
+    char *cic_var = rl_variable_value ("completion-ignore-case");
+    int cic = 1;
+    if (cic_var && STREQ (cic_var, "off"))
+      cic = 0;
+
+    int matches =
+      cic ? STRCASEEQLEN (word->name, text, len)
+          : STREQLEN (word->name, text, len);
+
+    if (matches) {
       if (word->is_dir)
         rl_completion_append_character = '/';
 
       if (word->is_dir)
         rl_completion_append_character = '/';