Sat, 28 Mar 2020 15:53:44 +0100
first draft for dav edit command
dav/main.c | file | annotate | diff | comparison | revisions | |
dav/main.h | file | annotate | diff | comparison | revisions |
--- a/dav/main.c Sun Dec 15 18:43:01 2019 +0100 +++ b/dav/main.c Sat Mar 28 15:53:44 2020 +0100 @@ -35,6 +35,7 @@ #include <unistd.h> #include <time.h> #include <sys/types.h> +#include <sys/wait.h> #include <ucx/string.h> #include <ucx/utils.h> #include <dirent.h> @@ -158,6 +159,8 @@ } else if(!strcasecmp(cmd, "cat")) { ucx_map_cstr_put(args->options, "output", "-"); ret = cmd_get(args, FALSE); + } else if(!strcasecmp(cmd, "edit")) { + ret = cmd_edit(args); } else if(!strcasecmp(cmd, "put")) { ret = cmd_put(args, FALSE); } else if( @@ -247,6 +250,7 @@ "list [-altdepcR] [-u <date>] <url>", "get [-pcRK] [-o <file>] [-u <date>] [-V <version>] <url>", "put [-pcR] [-k <key>] [-L <lock>] <url> <file...>", + "edit [-V <version>] <url>", "mkdir [-pc] [-k <key>] [-L <lock>] <url> [file...]", "remove [-pc] [-L <lock>] <url> [file...]", "copy [-pcO] [-L <lock>] <url> <url>", @@ -1115,6 +1119,160 @@ return ret; } +static int file_seek(FILE *f, curl_off_t offset, int origin) { + int ret = fseek(f, offset, origin); + return ret == 0 ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; +} + +int cmd_edit(CmdArgs *a) { + if(a->argc != 1) { + fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few":"many"); + fprintf(stderr, "Usage: dav %s\n", find_usage_str("edit")); + return -1; + } + + char *url = a->argv[0]; + char *path = NULL; + Repository *repo = url2repo(url, &path); + DavSession *sn = connect_to_repo(repo, path, a); + + if(set_session_config(sn, a)) { + return -1; + } + + char *version = cmd_getoption(a, "version"); + DavResource *res; + + res = dav_resource_new(sn, path); + int fresh_resource = !dav_exists(res); + if(fresh_resource) { + // create the resource to check if the resource can be created + // we don't want the user to invest time in editing and fail afterwards + if(dav_create(res)) { + fprintf(stderr, "Resource does not exist and cannot be created.\n"); + return -1; + } + } else { + if(!res) { + print_resource_error(sn, path); + return -1; + } + if(res->iscollection) { + fprintf(stderr, "Resource %s is a collection " + "and cannot be opened in an editor.\n", res->path); + return -1; + } + + if(version) { + DavResource *vres = find_version(res, version); + if(!vres) { + fprintf(stderr, "Cannot find version '%s' for resource.\n", version); + return -1; + } + dav_resource_free_all(res); + res = vres; + } + } + + // open temporary file + // TODO: look up the location of the temp directory, maybe add sys_* funcs + char outfile[24]; + strncpy(outfile, ".dav-edit-XXXXXX", 24); + int tmp_fd = mkstemp(outfile); + if(tmp_fd < 0) { + perror("Cannot open temporary file"); + return -1; + } + + // get resource + if(!fresh_resource) { + FILE* tmp_stream = sys_fopen(outfile, "wb"); + if(!tmp_stream) { + perror("Cannot open temporary file"); + close(tmp_fd); + return -1; + } + if(dav_get_content(res, tmp_stream, (dav_write_func)fwrite)) { + print_resource_error(sn, path); + close(tmp_fd); + unlink(outfile); + return -1; + } + fclose(tmp_stream); + } + + // remember time s.t. we can later check if the file has changed + SYS_STAT tmp_stat; + // TODO: maybe add sys_fstat + if(sys_stat(outfile, &tmp_stat)) { + perror("Cannot stat temporary file"); + close(tmp_fd); + unlink(outfile); + return -1; + } + time_t dl_mtime = tmp_stat.st_mtime; + + // open in editor + char* default_editor = "vi"; + char* editor = getenv("EDITOR"); + if(!editor) editor = default_editor; + char* viargs[3] = {editor, outfile, NULL}; + + int ret = 0; + // TODO: solution for windows + pid_t pid = fork(); + if(pid < 0) { + perror("Cannot create process for editor"); + ret = 1; + } else if(pid == 0) { + if(execvp(viargs[0], viargs)) { + perror("Opening the editor failed"); + return 1; + } + } else { + int status = -1; + ret = waitpid(pid, &status, 0); + if(ret < 0) { + perror("Error waiting for editor"); + } else if(WEXITSTATUS(status)) { + fprintf(stderr, + "Editor closed abnormally - file will not be uploaded.\n"); + ret = 1; + } else { + // check if the file has changed + if (sys_stat(outfile, &tmp_stat)) { + perror("Cannot stat temporary file"); + ret = 1; + } else if (dl_mtime < tmp_stat.st_mtime) { + // upload changed file + FILE* tmp_stream = sys_fopen(outfile, "rb"); + if(!tmp_stream) { + perror("Cannot open temporary file"); + close(tmp_fd); + return -1; + } + dav_set_content(res, tmp_stream, + (dav_read_func)fread, + (dav_seek_func)file_seek); + dav_set_content_length(res, tmp_stat.st_size); + ret = dav_store(res); + fclose(tmp_stream); + if(ret) { + print_resource_error(sn, path); + } + } else { + printf("No changes by user - file will not be uploaded.\n"); + } + } + } + + close(tmp_fd); + unlink(outfile); + free(path); + + return ret; +} + int get_resource(Repository *repo, GetResource *getres, CmdArgs *a, void *unused) { DavResource *res = getres->res; char *out = getres->path; @@ -1503,11 +1661,6 @@ return ret; } -static int file_seek(FILE *f, curl_off_t offset, int origin) { - int ret = fseek(f, offset, origin); - return ret == 0 ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK; -} - int put_file( Repository *repo, CmdArgs *a,