improve shell completion

Sat, 23 Nov 2019 15:14:50 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 23 Nov 2019 15:14:50 +0100
changeset 684
a4b4257c1a5f
parent 683
53bcb5e47220
child 685
487645580b5e

improve shell completion

dav/main.c file | annotate | diff | comparison | revisions
dav/main.h file | annotate | diff | comparison | revisions
dav/optparser.c file | annotate | diff | comparison | revisions
scripts/dav-bash-completion.bash file | annotate | diff | comparison | revisions
test/bin-test/test-dav-sync.sh file | annotate | diff | comparison | revisions
--- a/dav/main.c	Sat Nov 23 11:46:45 2019 +0100
+++ b/dav/main.c	Sat Nov 23 15:14:50 2019 +0100
@@ -186,28 +186,7 @@
                 || !strcasecmp(cmd, "--version")) {
             fprintf(stderr, "dav %s\n", DAV_VERSION);
         } else if(!strcasecmp(cmd, "complete")) {
-            if(args->argc < 2) {
-                return 1;
-            }
-            char *index_str = args->argv[0];
-            int64_t index = 0;
-            if(!util_strtoint(index_str, &index)) {
-                return 1;
-            }
-            if(args->argc + 2 != argc) {
-                // we have to fix the index
-                
-                for(int i=2;i<args->argc;i++) {
-                    if(index == i-2) {
-                        break;
-                    }
-                    if(strcmp(argv[i+2], args->argv[i])) {
-                        index--;
-                    }
-                }
-            }
-            
-            ret = shell_completion(args, index);
+            ret = cmd_complete(args);
         } else {
             print_usage(argv[0]);
         }
@@ -3002,15 +2981,98 @@
 }
 
 
-int shell_completion(CmdArgs *args, int index) {
-    if(args->argc < 2 || args->argc < 3) {
+static char** read_args_from_stdin(int *argc) {
+    // read stdin into buffer
+    UcxBuffer *in = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+    ucx_stream_copy(stdin, in, (read_func)fread, (write_func)ucx_buffer_write);
+    
+    // split input into lines
+    ssize_t count = 0;
+    sstr_t *lines = scstrsplit(scstrn(in->space, in->pos), SC("\n"), &count);
+    
+    char **args = NULL;
+    if(count > 0) {
+        args = calloc(count, sizeof(char*));
+        for(int i=0;i<count;i++) {
+            args[i] = lines[i].ptr;
+        }
+        free(lines);
+        
+        *argc = count;
+    } else {
+        *argc = 0;
+    }
+    
+    // cleanup
+    ucx_buffer_free(in);
+    
+    return args;
+}
+
+int cmd_complete(CmdArgs *args) {
+    if(args->argc != 1) {
+        return 1;
+    }
+    char *index_str = args->argv[0];
+    int64_t index = 0;
+    if(!util_strtoint(index_str, &index)) {
         return 1;
     }
     
+    // The completion bash script passes the input words to stdin
+    int comp_argc;
+    char **comp_argv = read_args_from_stdin(&comp_argc);
+        
+    // Try to parse the args
+    char *cmd = NULL;
+    if(comp_argc > 1) {
+        cmd = comp_argv[1];
+    }
+    CmdArgs *comp_args = cmd_parse_args(comp_argc - 2, comp_argv + 2);
+    if(comp_args) {
+        // check whether the arglist contains options
+        if(comp_args->argc + 2 != comp_argc) {
+            // index points to the arg in the raw arglist, however we have to
+            // know the index for this item in comp_args->argv
+            // any arg that is an option or an option value creates a
+            // difference between the two lists
+            
+            // adjust index to comp_args->argv
+            int j = 0;
+            for(int i=0;i<comp_argc-2;i++) {
+                if(index == i-2) {
+                    break;
+                }
+                
+                if(strcmp(comp_argv[i+2], comp_args->argv[j])) {
+                    index--;
+                } else {
+                    j++;
+                }
+            }
+        }
+    } else {
+        comp_args = NULL;
+    }
+    
+    // generate output for shell completion
+    int ret = 1;
+    if(comp_args) {
+        ret = shell_completion(cmd, comp_args, index);
+    }
+    
+    // cleanup
+    cmd_args_free(comp_args);
+    free(comp_argv);
+    return ret;
+    
+}
+
+int shell_completion(char *cmd, CmdArgs *args, int index) { 
     if(index == 1) {
         sstr_t prefix = { NULL, 0 };
-        if(args->argc > 2) {
-            prefix = sstr(args->argv[2]);
+        if(cmd) {
+            prefix = sstr(cmd);
         }
         for(int i=0;;i++) {
             char *str = cmdusageinfo[i];
@@ -3035,13 +3097,12 @@
         return 0;
     }
     
-    char *cmd = args->argv[2];
     if(!strcmp(cmd, "date")) {
         return 0;
     }
 
     // get already typed URL or NULL, if the user hasn't started typing yet
-    char *url = args->argc > 3 ? args->argv[3] : NULL;
+    char *url = args->argc > 0 ? args->argv[0] : NULL;
     
     //printf("index: {%s}\n", args->argv[0]);
     //printf("dav: {%s}\n", args->argv[1]);
@@ -3050,21 +3111,21 @@
     
     if(index == 2) {
         // url completion
-        return url_completion(url);
+        return url_completion(args, url);
     } else if (index == 3) {
         if(!strcmp(cmd, "put") || !strcmp(cmd, "import")) {
             // file completion
             return 12;            
         } else if(!strcmp(cmd, "copy") || !strcmp(cmd, "cp") || !strcmp(cmd, "move") || !strcmp(cmd, "mv")) {
             // url completion
-            return url_completion(url);
+            return url_completion(args, url);
         }
     }
     
     return 0;
 }
 
-int url_completion(char *u) {   
+int url_completion(CmdArgs *args, char *u) {   
     sstr_t url;
     url.ptr = u;
     url.length = u ? strlen(u) : 0;
@@ -3108,19 +3169,17 @@
         }
     } else {
         // url completion
-        
-        CmdArgs a;
-        memset(&a, 0, sizeof(CmdArgs));
-        a.options = ucx_map_new(4);
-        ucx_map_cstr_put(a.options, "noinput", "");
+        ucx_map_cstr_put(args->options, "noinput", "");
         
         char *path = NULL;
         Repository *repo = url2repo_s(url, &path);
-        DavSession *sn = connect_to_repo(repo, path, &a);
-        ucx_map_free(a.options);
+        DavSession *sn = connect_to_repo(repo, path, args);
         if(!sn) {
             return 0;
         }
+        if(set_session_config(sn, args)) {
+            return 0;
+        }
         
         size_t plen = strlen(path);
         
--- a/dav/main.h	Sat Nov 23 11:46:45 2019 +0100
+++ b/dav/main.h	Sat Nov 23 15:14:50 2019 +0100
@@ -135,9 +135,11 @@
 int cmd_remove_user(CmdArgs *args);
 int cmd_edit_user(CmdArgs *args);
 
-int shell_completion(CmdArgs *args, int index);
+int cmd_complete(CmdArgs *args);
 
-int url_completion(char *url);
+int shell_completion(char *cmd, CmdArgs *args, int index);
+
+int url_completion(CmdArgs *args, char *url);
 
 #ifdef	__cplusplus
 }
--- a/dav/optparser.c	Sat Nov 23 11:46:45 2019 +0100
+++ b/dav/optparser.c	Sat Nov 23 15:14:50 2019 +0100
@@ -36,7 +36,9 @@
 void cmd_args_free(CmdArgs *args) {
     if(args) {
         ucx_map_free(args->options);
-        free(args->argv);
+        if(args->argv) {
+            free(args->argv);
+        }
         free(args);
     }
 }
@@ -44,7 +46,7 @@
 CmdArgs* cmd_parse_args(int argc, char **argv) {
     CmdArgs *a = malloc(sizeof(CmdArgs));
     a->options = ucx_map_new(16);
-    a->argv = calloc(argc, sizeof(char*));
+    a->argv = argc > 0 ? calloc(argc, sizeof(char*)) : NULL;
     a->argc = 0;
     
     const char *NOARG = "";
--- a/scripts/dav-bash-completion.bash	Sat Nov 23 11:46:45 2019 +0100
+++ b/scripts/dav-bash-completion.bash	Sat Nov 23 15:14:50 2019 +0100
@@ -3,10 +3,10 @@
     for i in "${!COMP_WORDS[@]}"; do
         word="${COMP_WORDS[$i]}"
         if [[ "$word" =~ ^\'|^\" ]]; then
-            printf "%s\0" "$word"
+            printf "%s\n" "$word"
         else
             echo "$word" | xargs printf "%s"
-            printf "\0"
+            printf "\n"
         fi
     done
 }
@@ -17,12 +17,14 @@
     local cmd_res
 
     cmd="${COMP_WORDS[0]} complete $COMP_CWORD"
-    out=$(dav_completion_build_args | xargs -0 $cmd)
+    out=$(dav_completion_build_args | $cmd 2> /dev/null)
     cmd_res=$?
 
     if [[ $cmd_res -eq 10 ]]; then
         compopt -o nospace
-    fi
+	fi
+	
+	
     if [[ $cmd_res -eq 12 ]]; then
         compopt -o default
         COMPREPLY=()
--- a/test/bin-test/test-dav-sync.sh	Sat Nov 23 11:46:45 2019 +0100
+++ b/test/bin-test/test-dav-sync.sh	Sat Nov 23 15:14:50 2019 +0100
@@ -94,18 +94,18 @@
 #
 do_test "dav-sync push (1)" test-dav-sync-push1.sh
 do_test "dav-sync pull (1)" test-dav-sync-pull1.sh
-#do_test "dav-sync pull conflict (1)" test-dav-sync-pull-conflict.sh
-#do_test "dav-sync push conflict (1)" test-dav-sync-push-conflict.sh
-#do_test "dav-sync hashing (1)" test-dav-sync-hashing1.sh
-#do_test "dav-sync hashing (2)" test-dav-sync-hashing1.sh
-#do_test "dav-sync hash strategy" test-dav-sync-hash-strategy.sh
-#do_test "dav-sync hash conflict resolution" test-dav-sync-hash-conflictres.sh
-#do_test "dav-sync hashing change cfg" test-dav-sync-hashing-cfgchange.sh
-#do_test "dav-sync metadata (1)" test-dav-sync-metadata1.sh
-#do_test "dav-sync metadata (2)" test-dav-sync-metadata2.sh
-#do_test "dav-sync metadata (3)" test-dav-sync-metadata3.sh
-#do_test "dav-sync metadata (4)" test-dav-sync-metadata4.sh
-#do_test "dav-sync versioning (1)" test-dav-sync-versioning1.sh
+do_test "dav-sync pull conflict (1)" test-dav-sync-pull-conflict.sh
+do_test "dav-sync push conflict (1)" test-dav-sync-push-conflict.sh
+do_test "dav-sync hashing (1)" test-dav-sync-hashing1.sh
+do_test "dav-sync hashing (2)" test-dav-sync-hashing1.sh
+do_test "dav-sync hash strategy" test-dav-sync-hash-strategy.sh
+do_test "dav-sync hash conflict resolution" test-dav-sync-hash-conflictres.sh
+do_test "dav-sync hashing change cfg" test-dav-sync-hashing-cfgchange.sh
+do_test "dav-sync metadata (1)" test-dav-sync-metadata1.sh
+do_test "dav-sync metadata (2)" test-dav-sync-metadata2.sh
+do_test "dav-sync metadata (3)" test-dav-sync-metadata3.sh
+do_test "dav-sync metadata (4)" test-dav-sync-metadata4.sh
+do_test "dav-sync versioning (1)" test-dav-sync-versioning1.sh
 do_test "dav-sync split (1)" test-dav-sync-split1.sh
 
 # cleanup

mercurial