diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/main.c | 142 |
2 files changed, 132 insertions, 12 deletions
@@ -1,5 +1,5 @@ export FTAG_ROOT := $(shell pwd) -export __CFLAGS := -std=c99 -Wall -DFTAG_ROOT=\"$(FTAG_ROOT)\" +export __CFLAGS := -std=c99 -Wall -DFTAG_ROOT=\"$(FTAG_ROOT)\" -D_POSIX_C_SOURCE=200809L export __LIBS := $(shell pkg-config --libs sqlite3) export PROG ?= ftag @@ -33,12 +33,20 @@ static void __sqlite3_check(int rc, sqlite3 *db, const char *file, int line, con #define sqlite3_check(RC, DB) __sqlite3_check(RC, DB, __FILE__, __LINE__, __func__) -static void assert_db_exists(void) +static int file_exists(const char *path) { struct stat statbuf __attribute__((unused)); - int rc = stat(DATABASE_PATH, &statbuf); - + int rc = stat(path, &statbuf); if (rc == 0) + return 1; + else + return 0; +} + +static void assert_db_exists(void) +{ + errno = 0; + if (file_exists(DATABASE_PATH) && (errno == 0)) return; if (errno == ENOENT) { @@ -53,6 +61,12 @@ static void assert_db_exists(void) exit(EXIT_FAILURE); } +/* Make sure STR contains no single quote, not to break SQL queries. */ +static void assert_no_single_quote(const char *str) +{ + assert(strchr(str, '\'') == NULL); +} + static void ftag_init(int, char **) { char cmd[1024]; @@ -71,7 +85,7 @@ static int ftag_print(void *, int, char **cols, char **) return 0; } -static void ftag_list_table(const char *table) +static void ftag_list_table(const char *table, const char *col) { char sql[64]; sqlite3 *db = NULL; @@ -81,19 +95,123 @@ static void ftag_list_table(const char *table) sqlite3_check(rc, db); memset(sql, 0, sizeof(sql)); - snprintf(sql, sizeof(sql)-1, "SELECT name FROM %s;", table); + snprintf(sql, sizeof(sql)-1, "SELECT %s FROM %s;", col, table); rc = sqlite3_exec(db, sql, ftag_print, NULL, NULL); sqlite3_check(rc, db); + sqlite3_close(db); +} + +static void canonicalize(char *out, const char *in) +{ + int i; + for (i = 0; i < strlen(in)-1; i++) { + if (in[i] > 'A' && in[i] < 'Z') + out[i] = in[i] - 'A' + 'a'; + else if (in[i] == ' ') + out[i] = '_'; + else + out[i] = in[i]; + } + out[i] = '\0'; } +static void ftag_add_one_file(sqlite3 *db, + int *last_id, + const char *file) +{ + char sql[2048]; + int rc; + char *full_name = NULL; + char *canonical_name = NULL; + char *description = NULL; + size_t line_len = 0; + ssize_t read_len; + + if (!file_exists(file)) { + perror(file); + exit(EXIT_FAILURE); + } + + printf("ftag file add: adding file \"%s\" to database\n", file); + + printf("Enter the full name, a version of the name that\n" + "may contain blanks and every type of character.\n"); + read_len = getline(&full_name, &line_len, stdin); + if (read_len == -1) { + perror("getline"); + exit(EXIT_FAILURE); + } + assert_no_single_quote(full_name); + + line_len = read_len+1; + canonical_name = malloc(line_len); + canonicalize(canonical_name, full_name); + + printf("Enter the canonical name, a version of the name that\n" + "ideally contains no blank and, if possible, no upper\n" + "case letters and no symbols. If no input is given,\n" + "canonical name will be \"%s\".", canonical_name); + read_len = getline(&canonical_name, &line_len, stdin); + if (read_len == -1) { + perror("getline"); + exit(EXIT_FAILURE); + } + assert_no_single_quote(canonical_name); + + printf("Enter the description."); + line_len = 0; + read_len = getline(&description, &line_len, stdin); + if (read_len == -1) { + perror("getline"); + exit(EXIT_FAILURE); + } + assert_no_single_quote(description); + + memset(sql, 0, sizeof(sql)); + snprintf(sql, sizeof(sql)-1, + "INSERT INTO files VALUES(%d, '%s', '%s', '%s')", + *last_id, + canonical_name, + full_name, + description); + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + sqlite3_check(rc, db); + (*last_id)++; + free(full_name); + free(canonical_name); + if(description) + free(description); +} + +/* Add new files to the database. */ static void ftag_file_add(int argc, char **argv) { - ; + if (argc == 0) { + fprintf(stderr, "ftag file add: must supply file names\n"); + exit(EXIT_FAILURE); + } + sqlite3 *db = NULL; + int rc; + int last_id; + rc = sqlite3_open(DATABASE_PATH, &db); + sqlite3_check(rc, db); + for (int i = 0; i < argc; i++) + ftag_add_one_file(db, &last_id, argv[i]); + sqlite3_close(db); } static void ftag_file_list(int argc, char **argv) { - ftag_list_table("files"); + ftag_list_table("files", "full_name"); +} + +static void ftag_file_tag(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, + "ftag file tag: must supply a file name and tags\n"); + exit(EXIT_FAILURE); + } } static void ftag_file(int argc, char **argv) @@ -101,7 +219,8 @@ static void ftag_file(int argc, char **argv) assert_db_exists(); const struct ftag_command file_commands[] = { {.name = "add", .func = ftag_file_add}, - {.name = "list", .func = ftag_file_list} + {.name = "list", .func = ftag_file_list}, + {.name = "tag", .func = ftag_file_tag} }; const int file_command_count = sizeof(file_commands) / sizeof(struct ftag_command); parse_args(argc, argv, file_commands, file_command_count); @@ -152,7 +271,7 @@ static void ftag_tag_add(int argc, char **argv) } const char *new_tag_name = argv[0]; assert(strlen(new_tag_name) <= 255); - assert(strchr(new_tag_name, '\'') == NULL); + assert_no_single_quote(new_tag_name); char sql[1024]; sqlite3 *db = NULL; @@ -175,7 +294,7 @@ static void ftag_tag_add(int argc, char **argv) const char *new_tag_desc = argv[1]; assert(strlen(new_tag_desc) <= 600); - assert(strchr(new_tag_desc, '\'') == NULL); + assert_no_single_quote(new_tag_desc); last_id++; memset(sql, 0, sizeof(sql)); snprintf(sql, sizeof(sql)-1, @@ -183,11 +302,12 @@ static void ftag_tag_add(int argc, char **argv) last_id, new_tag_name, new_tag_desc); rc = sqlite3_exec(db, sql, NULL, NULL, NULL); sqlite3_check(rc, db); + sqlite3_close(db); } static void ftag_tag_list(int argc, char **argv) { - ftag_list_table("tags"); + ftag_list_table("tags", "name"); } static void ftag_tag(int argc, char **argv) |
