diff options
| author | Tristan Riehs <tristan.riehs@inria.fr> | 2026-07-05 18:31:01 +0200 |
|---|---|---|
| committer | Tristan Riehs <tristan.riehs@inria.fr> | 2026-07-05 18:31:16 +0200 |
| commit | 9b4d346d7d5831a2db2da9335509bd93b69e0840 (patch) | |
| tree | 710ba2b6a286efa941052242e4f7b8d5253be993 | |
| parent | 430bca8a0f166df268e2fa8fb207286640bd0602 (diff) | |
Outline of "ftag tag edit"
Some things are not finished yet, such as the handling of non-utf8
characters in SQL strings.
| -rw-r--r-- | src/main.c | 70 | ||||
| -rw-r--r-- | src/system.c | 46 | ||||
| -rw-r--r-- | src/system.h | 8 |
3 files changed, 123 insertions, 1 deletions
@@ -1010,11 +1010,79 @@ static void ftag_tag_add(int argc, char **argv) free(new_tag_desc); } +static int tag_edit_desc_callback(void *_old, int, char **cols, char **) +{ + char **old = _old; + assert(cols); + assert(cols[0]); + *old = strdup(cols[0]); + return 0; +} + +static void ftag_tag_edit(int argc, char **argv) +{ + if (argc < 1) { + fprintf(stderr, "Usage: ftag tag edit [-h] [-n] NAME...\n"); + exit(EXIT_FAILURE); + } + int opt; + int edit_name = 0; + char *column_name; + char *optstring = "hn"; + char *tag_name = NULL; + sqlite3 *db; + int rc; + char sql[512]; + char *old; + char *new; + optind = 1; + while ((opt = getopt(argc, argv, optstring)) != -1) { + switch (opt) { + case 'h': + printf("Usage: ftag tag edit [-h] [-n] NAME...\n"); + exit(EXIT_SUCCESS); + case 'n': + edit_name = 1; + break; + case '?': + default: + fprintf(stderr, "Usage: ftag tag edit [-h] [-n] NAME...\n"); + exit(EXIT_FAILURE); + } + } + tag_name = argv[optind]; + /* TODO: check that tag exists */ + db = ftag_db_open(); + if (edit_name) { + column_name = "name"; + old = strdup(tag_name); + } + else { + column_name = "description"; + strbuild(sql, "SELECT description FROM tags WHERE name = '%s';", + tag_name); + rc = sqlite3_exec(db, sql, tag_edit_desc_callback, &old, NULL); + sqlite3_check(rc, db); + } + new = edit(old); + printf("%s: old content: %s\n", __func__, old); + printf("%s: new content: %s\n", __func__, new); + sanitize_sql_str(&new); + /* TODO: fix isse with UTF-8 and sanitize_sql_str */ + strbuild(sql, "UPDATE tags SET %s = '%s' WHERE name = '%s';", column_name, new, tag_name); + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + sqlite3_check(rc, db); + free(old); + free(new); + sqlite3_close(db); +} + static void ftag_tag_help(int, char **) { puts("Usage: ftag tag COMMAND [ARG]..."); puts("Available values for COMMAND:"); puts(" add create new tags"); + puts(" edit edit a tag"); puts(" help print this help"); puts(" list list available tags"); } @@ -1029,10 +1097,10 @@ static void ftag_tag(int argc, char **argv) assert_db_exists(); const struct ftag_command tag_commands[] = { {.name = "add", .func = ftag_tag_add}, + {.name = "edit", .func = ftag_tag_edit}, {.name = "help", .func = ftag_tag_help}, {.name = "list", .func = ftag_tag_list} /* TODO: add an alias command */ - /* TODO: add an edit command */ }; int tag_command_count = sizeof(tag_commands) / sizeof(struct ftag_command); parse_args(argc - 1, argv + 1, tag_commands, tag_command_count); diff --git a/src/system.c b/src/system.c index 2f95661..ccb7ccd 100644 --- a/src/system.c +++ b/src/system.c @@ -122,3 +122,49 @@ int ftag_execvp(char *const *cmd, int can_exit) } return status; } + +char *edit(const char *initial_content) +{ + char tmp_file[32]; + int tmp_fd; + int rc; + char *env_editor; + char editor_cmd[512]; + char *cmd[] = { + "sh", + "-c", + editor_cmd, + NULL + }; + char *new_content; + strcpy(tmp_file, "/tmp/ftag-XXXXXX"); + tmp_fd = mkstemp(tmp_file); + sys_check(tmp_fd != -1, "mkstemp"); + rc = write(tmp_fd, initial_content, strlen(initial_content)); + sys_check(rc != -1, "write"); + env_editor = getenv("EDITOR"); + if (env_editor) + strbuild(editor_cmd, "%s %s", env_editor, tmp_file); + else + strbuild(editor_cmd, "%s %s", "nano", tmp_file); + ftag_execvp(cmd, 1); + new_content = file_to_string(tmp_fd); + close(tmp_fd); + return new_content; +} + +char *file_to_string(int fd) +{ + struct stat st; + char *content; + int rc; + rc = lseek(fd, 0L, SEEK_SET); + sys_check(rc != -1, "lseek"); + rc = fstat(fd, &st); + sys_check(rc != -1, "stat"); + content = malloc(st.st_size + 1); + rc = read(fd, content, st.st_size); + sys_check(rc != -1, "read"); + content[st.st_size] = '\0'; + return content; +} diff --git a/src/system.h b/src/system.h index 1045153..abc7c7f 100644 --- a/src/system.h +++ b/src/system.h @@ -31,4 +31,12 @@ void copy_file_with_encryption(const char *in, const char *out, enum encrypt enc * is non-zero, exit, else return -1. Return 0 on success. */ int ftag_execvp(char *const *cmd, int can_exit); +/* Create a temporary file containing INITIAL_CONTENT and open the editor to + * edit it, then return the edited string. */ +char *edit(const char *initial_content); + +/* Get the content of the open file FD as a heap-allocated string. The offset of + * FD is set to zero as a side effect. */ +char *file_to_string(int fd); + #endif |
