aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Riehs <tristan.riehs@inria.fr>2026-07-05 18:31:01 +0200
committerTristan Riehs <tristan.riehs@inria.fr>2026-07-05 18:31:16 +0200
commit9b4d346d7d5831a2db2da9335509bd93b69e0840 (patch)
tree710ba2b6a286efa941052242e4f7b8d5253be993
parent430bca8a0f166df268e2fa8fb207286640bd0602 (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.c70
-rw-r--r--src/system.c46
-rw-r--r--src/system.h8
3 files changed, 123 insertions, 1 deletions
diff --git a/src/main.c b/src/main.c
index a9ab73b..912cfde 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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