287 lines
8.1 KiB
C
287 lines
8.1 KiB
C
/*
|
|
Fonctions d'interaction avec la base de données
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sqlite3.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "include/colors.h"
|
|
#include "include/config.h"
|
|
#include "include/struct.h"
|
|
#include "include/utils.h"
|
|
#include "include/db.h"
|
|
|
|
|
|
#define sqlCheck(ret) { sqlAssert(ret, db, __FILE__, __LINE__); }
|
|
void sqlAssert(int ret, sqlite3* db, char* file, int line) {
|
|
if (ret != SQLITE_OK) {
|
|
printf( "%s:%d, error %d: %s\n", file, line, ret, sqlite3_errmsg(db));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
char* get_db_path() {
|
|
#ifndef DB_FILE
|
|
//! We should check that $HOME/.config already exists even if it should be the case
|
|
char* base_path = ".config/takl.sqlite3";
|
|
|
|
char* home_dir = getenv("HOME");
|
|
assert(home_dir != NULL);
|
|
|
|
char* db_path = malloc(sizeof(char)*(strlen(base_path)+strlen(home_dir)+1));
|
|
sprintf(db_path, "%s/%s", home_dir, base_path);
|
|
|
|
return db_path;
|
|
#else
|
|
char* db_path = malloc(sizeof(char)*(strlen(DB_FILE)+1));
|
|
memcpy(db_path, DB_FILE, sizeof(char)*(strlen(DB_FILE)+1));
|
|
|
|
return db_path;
|
|
#endif
|
|
}
|
|
|
|
|
|
sqlite3* get_db() {
|
|
sqlite3* db;
|
|
char* db_path = get_db_path();
|
|
|
|
if (access(db_path, F_OK) != 0) {
|
|
// Create DB
|
|
sqlite3_open(db_path, &db);
|
|
|
|
char* zErrMsg = NULL;
|
|
int ret = sqlite3_exec(
|
|
db,
|
|
"CREATE TABLE tasks ( \
|
|
id INTEGER PRIMARY KEY,\
|
|
text TEXT NOT NULL,\
|
|
category TEXT DEFAULT NULL, \
|
|
done INTEGER, \
|
|
due_to INTEGER \
|
|
);",
|
|
NULL,
|
|
NULL,
|
|
&zErrMsg
|
|
);
|
|
|
|
if (ret != SQLITE_OK) {
|
|
fprintf(stderr, "(db creation) SQL error: %s\n", zErrMsg);
|
|
sqlite3_free(zErrMsg);
|
|
}
|
|
|
|
sqlite3_close(db);
|
|
|
|
printf(GREEN "OK" RESET " Base de données créée\n");
|
|
}
|
|
|
|
int ret = sqlite3_open(db_path, &db);
|
|
if (ret != SQLITE_OK) {
|
|
fprintf(stderr, "(get_db) Unable to open db\n");
|
|
}
|
|
|
|
free(db_path);
|
|
return db;
|
|
}
|
|
|
|
void notify_change() {
|
|
char* socket_path = get_socket_path();
|
|
if (access(socket_path, F_OK) == 0) {
|
|
FILE* fp = fopen(socket_path, "r");
|
|
|
|
int pid;
|
|
fscanf(fp, "%d", &pid); // Trigger IN_ACCESS
|
|
(void)pid;
|
|
|
|
fclose(fp);
|
|
}
|
|
free(socket_path);
|
|
}
|
|
|
|
|
|
int add_task(task_t t) {
|
|
sqlite3* db = get_db();
|
|
sqlite3_stmt* stmt;
|
|
|
|
sqlCheck( sqlite3_prepare_v2(db, "INSERT INTO tasks (id, text, category, done, due_to) VALUES (?1, ?2, ?3, ?4, ?5);", -1, &stmt, 0) );
|
|
|
|
|
|
char str_id[10];
|
|
sprintf(str_id, "%d", (int)t.id);
|
|
sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
|
|
sqlCheck( sqlite3_bind_text(stmt, 2, t.text, -1, SQLITE_STATIC) );
|
|
sqlCheck( sqlite3_bind_text(stmt, 3, t.category, -1, SQLITE_STATIC) );
|
|
|
|
char str_done[2]; // Just a boolean
|
|
sprintf(str_done, "%d", t.done);
|
|
sqlCheck( sqlite3_bind_text(stmt, 4, str_done, -1, SQLITE_STATIC) );
|
|
|
|
char str_due_to[15];
|
|
sprintf(str_due_to, "%ld", t.due_to);
|
|
sqlCheck( sqlite3_bind_text(stmt, 5, str_due_to, -1, SQLITE_STATIC) );
|
|
|
|
|
|
sqlite3_step(stmt);
|
|
|
|
sqlCheck( sqlite3_finalize(stmt) );
|
|
sqlCheck( sqlite3_close(db) );
|
|
notify_change();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int update_task(task_t t) {
|
|
sqlite3* db = get_db();
|
|
sqlite3_stmt* stmt;
|
|
|
|
sqlCheck( sqlite3_prepare_v2(db, "UPDATE tasks SET text=?2, done=?3, due_to=?4 WHERE id=?1;", -1, &stmt, 0) );
|
|
|
|
|
|
char str_id[10];
|
|
sprintf(str_id, "%d", (int)t.id);
|
|
sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
|
|
sqlCheck( sqlite3_bind_text(stmt, 2, t.text, -1, SQLITE_STATIC) );
|
|
|
|
char str_done[2]; // Just a boolean
|
|
sprintf(str_done, "%d", t.done);
|
|
sqlCheck( sqlite3_bind_text(stmt, 3, str_done, -1, SQLITE_STATIC) );
|
|
|
|
char str_due_to[15];
|
|
sprintf(str_due_to, "%ld", t.due_to);
|
|
sqlCheck( sqlite3_bind_text(stmt, 4, str_due_to, -1, SQLITE_STATIC) );
|
|
|
|
sqlCheck( sqlite3_finalize(stmt) );
|
|
sqlCheck( sqlite3_close(db) );
|
|
notify_change();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void delete_task(int id) {
|
|
sqlite3* db = get_db();
|
|
sqlite3_stmt* stmt;
|
|
|
|
sqlCheck( sqlite3_prepare_v2(db, "DELETE FROM tasks WHERE id=?1;", -1, &stmt, 0) );
|
|
|
|
char str_id[10];
|
|
sprintf(str_id, "%d", id);
|
|
sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
|
|
|
|
sqlite3_step(stmt);
|
|
|
|
sqlCheck( sqlite3_finalize(stmt) );
|
|
sqlCheck( sqlite3_close(db) );
|
|
notify_change();
|
|
}
|
|
|
|
|
|
void get_task(int id, task_t* t) {
|
|
t->id = -1;
|
|
|
|
sqlite3* db = get_db();
|
|
sqlite3_stmt* stmt;
|
|
|
|
sqlCheck( sqlite3_prepare_v2(db, "SELECT id, text, done, due_to, category FROM tasks WHERE id=?1;", -1, &stmt, NULL) );
|
|
|
|
char str_id[10];
|
|
sprintf(str_id, "%d", id);
|
|
sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
|
|
|
|
int ret = sqlite3_step(stmt);
|
|
if (ret == SQLITE_ROW) {
|
|
t->id = strtol((char*)sqlite3_column_text(stmt, 0), NULL, 10);
|
|
t->done = strtol((char*)sqlite3_column_text(stmt, 2), NULL, 10);
|
|
t->due_to = strtol((char*)sqlite3_column_text(stmt, 3), NULL, 10);
|
|
|
|
char* text = (char*)sqlite3_column_text(stmt, 1);
|
|
t->text = malloc(sizeof(char)*(strlen(text)+1));
|
|
strcpy(t->text, text);
|
|
|
|
char* category = (char*)sqlite3_column_text(stmt, 4);
|
|
if (category) {
|
|
t->category = malloc(sizeof(char)*(strlen(category)+1));
|
|
strcpy(t->category, category);
|
|
} else {
|
|
t->category = NULL;
|
|
}
|
|
}
|
|
|
|
sqlCheck( sqlite3_finalize(stmt) );
|
|
sqlCheck( sqlite3_close(db) );
|
|
|
|
// t->id will be (-1) if not found
|
|
}
|
|
|
|
task_list_t* get_task_list(char* input_category, bool include_completed) {
|
|
sqlite3* db = get_db();
|
|
sqlite3_stmt *stmt;
|
|
|
|
if (!input_category) {
|
|
sqlCheck( sqlite3_prepare_v2(db, "SELECT id, text, done, due_to, category FROM tasks;", -1, &stmt, NULL) );
|
|
} else {
|
|
sqlCheck( sqlite3_prepare_v2(db, "SELECT id, text, done, due_to, category FROM tasks WHERE category=?1;", -1, &stmt, NULL) );
|
|
sqlCheck( sqlite3_bind_text(stmt, 1, input_category, -1, SQLITE_STATIC) );
|
|
}
|
|
|
|
|
|
task_list_t* list = NULL;
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
task_t t;
|
|
|
|
t.id = strtol((char*)sqlite3_column_text(stmt, 0), NULL, 10);
|
|
t.done = strtol((char*)sqlite3_column_text(stmt, 2), NULL, 10);
|
|
t.due_to = strtol((char*)sqlite3_column_text(stmt, 3), NULL, 10);
|
|
|
|
if (!t.done || include_completed) { // We don't want to allocate memory for things that will not be used
|
|
char* text = (char*)sqlite3_column_text(stmt, 1);
|
|
t.text = malloc(sizeof(char)*(strlen(text)+1));
|
|
strcpy(t.text, text);
|
|
|
|
char* category = (char*)sqlite3_column_text(stmt, 4);
|
|
if (category) {
|
|
t.category = malloc(sizeof(char)*(strlen(category)+1));
|
|
strcpy(t.category, category);
|
|
} else {
|
|
t.category = NULL;
|
|
}
|
|
|
|
|
|
task_list_t* cur = malloc(sizeof(task_list_t)); // Add the parsed task to the beginning of the list
|
|
cur->task = t;
|
|
cur->next = list;
|
|
list = cur;
|
|
}
|
|
}
|
|
|
|
sqlCheck( sqlite3_finalize(stmt) );
|
|
sqlCheck( sqlite3_close(db) );
|
|
|
|
return list;
|
|
}
|
|
|
|
int get_new_task_id() {
|
|
task_t t;
|
|
|
|
int new_id = rand() %MAX_TASK_ID;
|
|
t.id = new_id;
|
|
|
|
int i=0;
|
|
while (t.id != -1 && i < NEW_TASK_ID_MAX_RETRIES) { // If t.id==-1, task is not found so the id is sage to use
|
|
new_id = rand() %MAX_TASK_ID;
|
|
get_task(new_id, &t);
|
|
i++;
|
|
}
|
|
|
|
if (i == NEW_TASK_ID_MAX_RETRIES) {
|
|
printf(RED BOLD "Il semblerait que vous ayez un nombre de tâches se rapprochant de %d.\n" RESET, MAX_TASK_ID);
|
|
printf(YELLOW "Il peut être judicieux de supprimer les tâches effectuées pour libérer de la place (et des identifiants).\n");
|
|
printf("Vous pouvez également modifier la limite du nombre d'identifiants et recompiler le projet.\n" RESET);
|
|
return -1;
|
|
}
|
|
|
|
return new_id;
|
|
} |