# HG changeset patch # User Olaf Wintermann # Date 1574518490 -3600 # Node ID a4b4257c1a5f06f43e2514ba8d7c6560257e38f4 # Parent 53bcb5e47220fde098626bf303e5909e5aaf73f0 improve shell completion diff -r 53bcb5e47220 -r a4b4257c1a5f dav/main.c --- 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;iargc;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;iargc != 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;iargv[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); diff -r 53bcb5e47220 -r a4b4257c1a5f dav/main.h --- 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 } diff -r 53bcb5e47220 -r a4b4257c1a5f dav/optparser.c --- 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 = ""; diff -r 53bcb5e47220 -r a4b4257c1a5f scripts/dav-bash-completion.bash --- 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=() diff -r 53bcb5e47220 -r a4b4257c1a5f test/bin-test/test-dav-sync.sh --- 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