Compare commits

...

14 Commits
v1.1.0 ... main

15 changed files with 282 additions and 147 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
takl.sqlite3 takl.sqlite3
takl takl
takl-daemon takl-daemon
takl.sock
out out
.vscode .vscode

View File

@ -1,5 +1,6 @@
FLAGS = -g -Wall -Wextra -DLOG_USE_COLOR `pkg-config --cflags --libs libnotify` FLAGS = -g -Wall -Wextra -DLOG_USE_COLOR
LD_FLAGS = -lsqlite3 -lpthread LD_FLAGS = -lsqlite3 -lpthread
NOTIF_FLAGS = `pkg-config --cflags --libs libnotify`
$(shell mkdir -p out) $(shell mkdir -p out)
@ -11,6 +12,9 @@ all: takl takl-daemon
out/%.o: src/%.c out/%.o: src/%.c
$(CC) -c $(FLAGS) $^ -o $@ $(CC) -c $(FLAGS) $^ -o $@
out/notification.o: src/main/notification.c
$(CC) -c $(FLAGS) $(NOTIF_FLAGS) $^ -o $@
out/%.o: src/main/%.c out/%.o: src/main/%.c
$(CC) -c $(FLAGS) $^ -o $@ $(CC) -c $(FLAGS) $^ -o $@
@ -20,8 +24,8 @@ out/%.o: src/log/%.c
takl: out/main.o out/db.o out/tasks.o out/utils.o takl: out/main.o out/db.o out/tasks.o out/utils.o
$(CC) $(FLAGS) $(LD_FLAGS) $^ -o $@ $(CC) $(FLAGS) $(LD_FLAGS) $^ -o $@
takl-daemon: out/daemon.o out/db.o out/tasks.o out/utils.o out/log.o takl-daemon: out/daemon.o out/db.o out/tasks.o out/utils.o out/log.o out/notification.o
$(CC) $(FLAGS) $(LD_FLAGS) $^ -o $@ $(CC) $(FLAGS) $(LD_FLAGS) $(NOTIF_FLAGS) $^ -o $@
install: takl takl-daemon install: takl takl-daemon

View File

@ -5,7 +5,7 @@ facile d'utilisation et donnant rapidement les choses demandées
### Installation ### Installation
Les seules dépendances nécessaires sont `gcc` et `g++`. Les seules dépendances nécessaires sont `gcc` et `libnotify`.
(Peut-être que des bibliothèques comme sqlite3 sont à installer sur certaines distributions/ OS) (Peut-être que des bibliothèques comme sqlite3 sont à installer sur certaines distributions/ OS)
```bash ```bash
@ -24,14 +24,17 @@ Pour gnome, `make gnome-install` lancera le programme à tous les prochains red
### Utilisation ### Utilisation
``` ```
Utilisation: takl ( list | add | info | rm | done ) Utilisation: takl ( list | add | reschedule | info | rm | done )
list [-a:voir les tâches complétées] list [category] [-a:voir les tâches complétées]
add <task> [date] add [category:]<task> [date]
Format de la date: reschedule <id> [date]
Relatif: min+%d, h+%d, j+%d
Absolu: dd/mm (pas de changement d'année pour l'instant)
info <id1> <id2> ... info <id1> <id2> ...
done <id1> <id2> ... done <id1> <id2> ...
rm <id1> <id2> ... rm <id1> <id2> ...
Format des dates:
Relatif: min+%d, h+%d, j+%d
Absolu: dd/mm (pas de changement d'année pour l'instant)
``` ```
Appeler l'exécutable sans arguments vous renverra cette petite liste de commandes que vous pouvez utiliser. Appeler l'exécutable sans arguments vous renverra cette petite liste de commandes que vous pouvez utiliser.

View File

@ -14,7 +14,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <pthread.h> #include <pthread.h>
#include <sys/types.h> #include <sys/types.h>
#include <linux/inotify.h> #include <sys/inotify.h>
#include "log/log.h" #include "log/log.h"
#include "main/include/db.h" #include "main/include/db.h"
@ -22,6 +22,7 @@
#include "main/include/utils.h" #include "main/include/utils.h"
#include "main/include/colors.h" #include "main/include/colors.h"
#include "main/include/config.h" #include "main/include/config.h"
#include "main/include/notification.h"
#define EVENT_SIZE ( sizeof (struct inotify_event) ) #define EVENT_SIZE ( sizeof (struct inotify_event) )
@ -35,10 +36,9 @@ pthread_mutex_t tasks_lock = PTHREAD_MUTEX_INITIALIZER; // locks tasks and has_c
void* inotify_check_changes(void* arg) { void* inotify_check_changes(void* sk_path) {
(void)arg; char* socket_path = (char*)sk_path;
char buffer[EVENT_BUF_LEN]; char buffer[EVENT_BUF_LEN];
char* socket_path = get_socket_path();
while (1) { // Regarde si /tmp/takl.$USER a changé (donc si la BDD a changé et doit être rechargée) while (1) { // Regarde si /tmp/takl.$USER a changé (donc si la BDD a changé et doit être rechargée)
int fd = inotify_init(); int fd = inotify_init();
@ -60,21 +60,18 @@ void* inotify_check_changes(void* arg) {
log_debug("Base de données changée"); log_debug("Base de données changée");
pthread_mutex_lock(&tasks_lock); pthread_mutex_lock(&tasks_lock);
free_task_list(tasks); free_task_list(tasks);
tasks = get_task_list(false); tasks = get_task_list(false, false);
has_changed = true; has_changed = true;
pthread_mutex_unlock(&tasks_lock); pthread_mutex_unlock(&tasks_lock);
inotify_rm_watch(fd, wd); inotify_rm_watch(fd, wd);
close(fd); close(fd);
} }
free(socket_path);
return NULL; return NULL;
} }
int existing_takl_daemon() { int existing_takl_daemon(char* socket_path) {
char* socket_path = get_socket_path();
if (!access(socket_path, F_OK)) { // tmp file exists if (!access(socket_path, F_OK)) { // tmp file exists
FILE* fp = fopen(socket_path, "r+"); FILE* fp = fopen(socket_path, "r+");
int daemon_pid; int daemon_pid;
@ -90,25 +87,26 @@ int existing_takl_daemon() {
fprintf(fp, "%d", getpid()); fprintf(fp, "%d", getpid());
fclose(fp); fclose(fp);
free(socket_path);
return false; return false;
} }
int main() { int main() {
bool notified_no_change = false; bool notified_no_change = false;
char* socket_path = get_socket_path();
log_debug("Socket path:%s", socket_path);
if (!existing_takl_daemon()) { if (!existing_takl_daemon(socket_path)) {
log_info("TaKl " VERSION " -- Daemon started"); log_info("TaKl " VERSION " -- Daemon started");
} else { } else {
log_info("TaKl Daemon déjà en cours d'exécution. Arrêt"); log_info("TaKl Daemon déjà en cours d'exécution. Arrêt");
return 1; return 1;
} }
tasks = get_task_list(false); tasks = get_task_list(false, false);
pthread_t inotify_id = 0; // Lancement d'inotify dans un autre fil pthread_t inotify_id = 0; // Lancement d'inotify dans un autre fil
pthread_create(&inotify_id, NULL, inotify_check_changes, NULL); pthread_create(&inotify_id, NULL, inotify_check_changes, (void*)socket_path);
while (1) { while (1) {
pthread_mutex_lock(&tasks_lock); pthread_mutex_lock(&tasks_lock);
@ -169,5 +167,6 @@ int main() {
pthread_join(inotify_id, NULL); pthread_join(inotify_id, NULL);
free_task_list(tasks); free_task_list(tasks);
free(socket_path);
return 0; return 0;
} }

View File

@ -11,8 +11,8 @@
void help(char* name) { void help(char* name) {
printf(BLUE "\t-- TaKl " VERSION " --\n" RESET); printf(BLUE "\t-- TaKl " VERSION " --\n" RESET);
printf("Utilisation: %s ( list | add | reschedule | info | rm | done )\n", name); printf("Utilisation: %s ( list | add | reschedule | info | rm | done )\n", name);
printf("\tlist [-a:voir les tâches complétées]\n"); printf("\tlist [category] [-a:voir les tâches complétées]\n");
printf("\tadd <task> [date]\n"); printf("\tadd [category:]<task> [date]\n");
printf("\treschedule <id> [date]\n"); printf("\treschedule <id> [date]\n");
printf("\tinfo <id1> <id2> ...\n"); printf("\tinfo <id1> <id2> ...\n");
printf("\tdone <id1> <id2> ...\n"); printf("\tdone <id1> <id2> ...\n");
@ -35,7 +35,7 @@ time_t parseDateTime(const char* input) {
date.tm_year = current_time->tm_year; date.tm_year = current_time->tm_year;
date.tm_mon = current_time->tm_mon; date.tm_mon = current_time->tm_mon;
date.tm_mday = current_time->tm_mday; date.tm_mday = current_time->tm_mday;
date.tm_hour = current_time->tm_hour; date.tm_hour = current_time->tm_hour-1; //TODO: buggy in non-UTC
date.tm_min = current_time->tm_min; date.tm_min = current_time->tm_min;
date.tm_sec = current_time->tm_sec; date.tm_sec = current_time->tm_sec;
@ -88,14 +88,27 @@ int add(int argc, char* argv[]) {
task_t t = create_task(argv[2], due_to); task_t t = create_task(argv[2], due_to);
if (t.id == -1) { if (t.id == -1) {
if (t.category) {
free(t.category);
free(t.text);
}
return 1; return 1;
} }
if (add_task(t) != 0) { if (add_task(t) != 0) {
printf("Erreur lors de l'ajout à la base de données\n"); printf("Erreur lors de l'ajout à la base de données\n");
if (t.category) {
free(t.category);
free(t.text);
}
return 1; return 1;
} }
if (t.category) {
free(t.category);
free(t.text);
}
printf(BOLD YELLOW "[%d]" RESET " ajoutée\n", t.id); printf(BOLD YELLOW "[%d]" RESET " ajoutée\n", t.id);
return 0; return 0;
} }
@ -135,9 +148,20 @@ int reschedule(int argc, char* argv[]) {
return 0; return 0;
} }
int list_tasks(bool show_completed) { int list_tasks(int argc, char* argv[]) {
task_list_t* tasks = get_task_list(show_completed); bool show_completed = false;
print_task_list(tasks, show_completed); // show_completed: true would work as all the tasks are not loaded if false char* category = NULL;
for (int i=2; i < argc; i++) {
if (!strcmp(argv[i], "-a")) {
show_completed = true;
} else {
category = argv[i];
}
}
task_list_t* tasks = get_task_list(category, show_completed);
print_task_list(tasks, !category, show_completed); // show_completed: true would work as all the tasks are not loaded if false
free_task_list(tasks); free_task_list(tasks);
return 0; return 0;
@ -165,6 +189,9 @@ int info(int argc, char* argv[]) {
print_task(t); print_task(t);
free(t.text); free(t.text);
if (t.category) {
free(t.category);
}
} }
return 0; return 0;
@ -235,7 +262,7 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(argv[1], "reschedule")) { } else if (!strcmp(argv[1], "reschedule")) {
return reschedule(argc, argv); return reschedule(argc, argv);
} else if (!strcmp(argv[1], "list")) { } else if (!strcmp(argv[1], "list")) {
return list_tasks(argc > 2 && !strcmp(argv[2], "-a")); return list_tasks(argc, argv);
} else if (!strcmp(argv[1], "info")) { } else if (!strcmp(argv[1], "info")) {
return info(argc, argv); return info(argc, argv);
} else if (!strcmp(argv[1], "done")) { } else if (!strcmp(argv[1], "done")) {

View File

@ -15,9 +15,27 @@ Fonctions d'interaction avec la base de données
#include "include/db.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() { char* get_db_path() {
#ifndef DB_FILE /*
//! We should check that $HOME/.config already exists even if it should be the case Checks if $TAKL_DB env variable is set
else, the DB is located at $HOME/.config/takl.sqlite3
*/
char* env_db_path = getenv("TAKL_DB");
if (env_db_path) {
char* db_path = malloc(sizeof(char)*(strlen(env_db_path)+1));
strcpy(db_path, env_db_path);
return db_path;
} else {
char* base_path = ".config/takl.sqlite3"; char* base_path = ".config/takl.sqlite3";
char* home_dir = getenv("HOME"); char* home_dir = getenv("HOME");
@ -27,12 +45,7 @@ char* get_db_path() {
sprintf(db_path, "%s/%s", home_dir, base_path); sprintf(db_path, "%s/%s", home_dir, base_path);
return db_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
} }
@ -50,6 +63,7 @@ sqlite3* get_db() {
"CREATE TABLE tasks ( \ "CREATE TABLE tasks ( \
id INTEGER PRIMARY KEY,\ id INTEGER PRIMARY KEY,\
text TEXT NOT NULL,\ text TEXT NOT NULL,\
category TEXT DEFAULT NULL, \
done INTEGER, \ done INTEGER, \
due_to INTEGER \ due_to INTEGER \
);", );",
@ -79,13 +93,16 @@ sqlite3* get_db() {
void notify_change() { void notify_change() {
char* socket_path = get_socket_path(); char* socket_path = get_socket_path();
FILE* fp = fopen(socket_path, "r"); 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); int pid;
fscanf(fp, "%d", &pid); // Trigger IN_ACCESS
(void)pid;
fclose(fp);
}
free(socket_path);
} }
@ -93,42 +110,30 @@ int add_task(task_t t) {
sqlite3* db = get_db(); sqlite3* db = get_db();
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
int ret = sqlite3_prepare_v2(db, "INSERT INTO tasks (id, text, done, due_to) VALUES (?1, ?2, ?3, ?4);", -1, &stmt, 0); sqlCheck( sqlite3_prepare_v2(db, "INSERT INTO tasks (id, text, category, done, due_to) VALUES (?1, ?2, ?3, ?4, ?5);", -1, &stmt, 0) );
if (ret != SQLITE_OK) {
printf("(get_task) failure fetching data\n");
return 1;
}
char str_id[10]; char str_id[10];
sprintf(str_id, "%d", (int)t.id); sprintf(str_id, "%d", (int)t.id);
sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
sqlite3_bind_text(stmt, 2, t.text, -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 char str_done[2]; // Just a boolean
sprintf(str_done, "%d", t.done); sprintf(str_done, "%d", t.done);
sqlite3_bind_text(stmt, 3, str_done, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 4, str_done, -1, SQLITE_STATIC) );
char str_due_to[15]; char str_due_to[15];
sprintf(str_due_to, "%ld", t.due_to); sprintf(str_due_to, "%ld", t.due_to);
sqlite3_bind_text(stmt, 4, str_due_to, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 5, str_due_to, -1, SQLITE_STATIC) );
ret = sqlite3_step(stmt); sqlite3_step(stmt);
sqlite3_finalize(stmt); sqlCheck( sqlite3_finalize(stmt) );
sqlite3_close(db); sqlCheck( sqlite3_close(db) );
notify_change(); notify_change();
if (ret != SQLITE_DONE) {
if (ret == 19) {
printf("Identifiant déjà utilisé\n");
} else {
printf("Return value: %d\n", ret);
}
return 1;
}
return 0; return 0;
} }
@ -136,37 +141,28 @@ int update_task(task_t t) {
sqlite3* db = get_db(); sqlite3* db = get_db();
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
int ret = sqlite3_prepare_v2(db, "UPDATE tasks SET text=?2, done=?3, due_to=?4 WHERE id=?1;", -1, &stmt, 0); sqlCheck( sqlite3_prepare_v2(db, "UPDATE tasks SET text=?2, done=?3, due_to=?4 WHERE id=?1;", -1, &stmt, 0) );
if (ret != SQLITE_OK) {
printf("(get_task) failure fetching data\n");
return 1;
}
char str_id[10]; char str_id[10];
sprintf(str_id, "%d", (int)t.id); sprintf(str_id, "%d", (int)t.id);
sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
sqlite3_bind_text(stmt, 2, t.text, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 2, t.text, -1, SQLITE_STATIC) );
char str_done[2]; // Just a boolean char str_done[2]; // Just a boolean
sprintf(str_done, "%d", t.done); sprintf(str_done, "%d", t.done);
sqlite3_bind_text(stmt, 3, str_done, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 3, str_done, -1, SQLITE_STATIC) );
char str_due_to[15]; char str_due_to[15];
sprintf(str_due_to, "%ld", t.due_to); sprintf(str_due_to, "%ld", t.due_to);
sqlite3_bind_text(stmt, 4, str_due_to, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 4, str_due_to, -1, SQLITE_STATIC) );
sqlite3_step(stmt);
ret = sqlite3_step(stmt); sqlCheck( sqlite3_finalize(stmt) );
sqlCheck( sqlite3_close(db) );
sqlite3_finalize(stmt);
sqlite3_close(db);
notify_change(); notify_change();
if (ret != SQLITE_DONE) {
printf("Return value: %d\n", ret);
return 1;
}
return 0; return 0;
} }
@ -175,21 +171,16 @@ void delete_task(int id) {
sqlite3* db = get_db(); sqlite3* db = get_db();
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
int ret = sqlite3_prepare_v2(db, "DELETE FROM tasks WHERE id=?1;", -1, &stmt, 0); sqlCheck( sqlite3_prepare_v2(db, "DELETE FROM tasks WHERE id=?1;", -1, &stmt, 0) );
if (ret != SQLITE_OK) {
printf("(get_task) failure fetching data\n");
exit(1);
}
char str_id[10]; char str_id[10];
sprintf(str_id, "%d", id); sprintf(str_id, "%d", id);
sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
sqlite3_step(stmt); sqlite3_step(stmt);
sqlite3_finalize(stmt); sqlCheck( sqlite3_finalize(stmt) );
sqlite3_close(db); sqlCheck( sqlite3_close(db) );
notify_change(); notify_change();
} }
@ -200,19 +191,13 @@ void get_task(int id, task_t* t) {
sqlite3* db = get_db(); sqlite3* db = get_db();
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
int ret = sqlite3_prepare_v2(db, "SELECT id, text, done, due_to FROM tasks WHERE id=?1;", -1, &stmt, 0); sqlCheck( sqlite3_prepare_v2(db, "SELECT id, text, done, due_to, category FROM tasks WHERE id=?1;", -1, &stmt, NULL) );
if (ret != SQLITE_OK) {
printf("(get_task) failure fetching data\n");
exit(1);
}
char str_id[10]; char str_id[10];
sprintf(str_id, "%d", id); sprintf(str_id, "%d", id);
sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC); sqlCheck( sqlite3_bind_text(stmt, 1, str_id, -1, SQLITE_STATIC) );
ret = sqlite3_step(stmt); int ret = sqlite3_step(stmt);
if (ret == SQLITE_ROW) { if (ret == SQLITE_ROW) {
t->id = strtol((char*)sqlite3_column_text(stmt, 0), NULL, 10); t->id = strtol((char*)sqlite3_column_text(stmt, 0), NULL, 10);
t->done = strtol((char*)sqlite3_column_text(stmt, 2), NULL, 10); t->done = strtol((char*)sqlite3_column_text(stmt, 2), NULL, 10);
@ -221,24 +206,33 @@ void get_task(int id, task_t* t) {
char* text = (char*)sqlite3_column_text(stmt, 1); char* text = (char*)sqlite3_column_text(stmt, 1);
t->text = malloc(sizeof(char)*(strlen(text)+1)); t->text = malloc(sizeof(char)*(strlen(text)+1));
strcpy(t->text, text); 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;
}
} }
sqlite3_finalize(stmt); sqlCheck( sqlite3_finalize(stmt) );
sqlite3_close(db); sqlCheck( sqlite3_close(db) );
// t->id will be (-1) if not found // t->id will be (-1) if not found
} }
task_list_t* get_task_list(bool include_completed) { task_list_t* get_task_list(char* input_category, bool include_completed) {
sqlite3* db = get_db(); sqlite3* db = get_db();
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
int ret = sqlite3_prepare_v2(db, "SELECT id, text, done, due_to FROM tasks;", -1, &stmt, NULL); if (!input_category) {
sqlCheck( sqlite3_prepare_v2(db, "SELECT id, text, done, due_to, category FROM tasks;", -1, &stmt, NULL) );
if (ret != SQLITE_OK) { } else {
printf("(get_task) failure fetching data\n"); sqlCheck( sqlite3_prepare_v2(db, "SELECT id, text, done, due_to, category FROM tasks WHERE category=?1;", -1, &stmt, NULL) );
return NULL; sqlCheck( sqlite3_bind_text(stmt, 1, input_category, -1, SQLITE_STATIC) );
} }
task_list_t* list = NULL; task_list_t* list = NULL;
while (sqlite3_step(stmt) == SQLITE_ROW) { while (sqlite3_step(stmt) == SQLITE_ROW) {
@ -253,6 +247,15 @@ task_list_t* get_task_list(bool include_completed) {
t.text = malloc(sizeof(char)*(strlen(text)+1)); t.text = malloc(sizeof(char)*(strlen(text)+1));
strcpy(t.text, text); 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 task_list_t* cur = malloc(sizeof(task_list_t)); // Add the parsed task to the beginning of the list
cur->task = t; cur->task = t;
cur->next = list; cur->next = list;
@ -260,8 +263,8 @@ task_list_t* get_task_list(bool include_completed) {
} }
} }
sqlite3_finalize(stmt); sqlCheck( sqlite3_finalize(stmt) );
sqlite3_close(db); sqlCheck( sqlite3_close(db) );
return list; return list;
} }

View File

@ -1,15 +1,11 @@
#ifndef DEF_CONFIG_H #ifndef DEF_CONFIG_H
#define DEF_CONFIG_H #define DEF_CONFIG_H
#define VERSION "1.1.0" #define VERSION "1.3.5"
// By default, $HOME/.config/takl.sqlite3 is used. You can change this behaviour here
//#define DB_FILE "takl.sqlite3"
// By default, /tmp/takl.$USER is used. You can change this behaviour here
//#define SOCKET_FILE "takl.sock"
#define MAX_TASK_ID 10000 // max is set to MAX_TASK_ID-1 #define MAX_TASK_ID 10000 // max is set to MAX_TASK_ID-1
#define NEW_TASK_ID_MAX_RETRIES 10000 // number of retries before giving up #define NEW_TASK_ID_MAX_RETRIES 10000 // number of retries before giving up
#define TMPDIR "/tmp" // Some Unix systems don't use /tmp as tmp dir (eg Android)
#endif #endif

View File

@ -40,7 +40,7 @@ void get_task(int id, task_t* t);
/* /*
Renvoie la liste des tâches de la base de données Renvoie la liste des tâches de la base de données
*/ */
task_list_t* get_task_list(bool include_completed); task_list_t* get_task_list(char* input_category, bool include_completed);
/* /*
Renvoie un identifiant de tâche encore non utilisé Renvoie un identifiant de tâche encore non utilisé

View File

@ -0,0 +1,10 @@
#ifndef DEF_NOTIFICATION_H
#define DEF_NOTIFICATION_H
#include "tasks.h"
/*
Envoyer une notification avec t
*/
void desktop_notification(task_t t);
#endif

View File

@ -7,6 +7,7 @@
struct task { struct task {
int id; int id;
char* text; char* text;
char* category;
bool done; bool done;
time_t due_to; time_t due_to;
}; };

View File

@ -3,6 +3,12 @@
#ifndef DEF_TASKS_H #ifndef DEF_TASKS_H
#define DEF_TASKS_H #define DEF_TASKS_H
/*
Sépare la chaîne d'entrée en deux chaînes, l'une avant, l'autre après le premier ':'.
Si il n'y a pas de ':', out_category est NULL
*/
void parse_input(char* in_text, char** out_text, char** out_category);
/* /*
Renvoie une tâche avec un identifiant aléatoire valide Renvoie une tâche avec un identifiant aléatoire valide
*/ */
@ -16,16 +22,11 @@ void print_task(task_t task);
/* /*
Affiche une liste de tâches de manière compacte (et par catégorie: urgent, à faire, [complétée]) Affiche une liste de tâches de manière compacte (et par catégorie: urgent, à faire, [complétée])
*/ */
void print_task_list(task_list_t* list, bool show_completed); void print_task_list(task_list_t* list, bool show_category, bool show_completed);
/* /*
Libère la mémoire allouée à une liste de tâches Libère la mémoire allouée à une liste de tâches
*/ */
void free_task_list(task_list_t* list); void free_task_list(task_list_t* list);
/*
Envoyer une notification avec t
*/
void desktop_notification(task_t t);
#endif #endif

11
src/main/notification.c Normal file
View File

@ -0,0 +1,11 @@
#include <libnotify/notify.h>
#include "include/notification.h"
void desktop_notification(task_t t) {
notify_init ("TaKl");
NotifyNotification * Notif = notify_notification_new ("TaKl Daemon", t.text, "dialog-information");
notify_notification_show (Notif, NULL);
g_object_unref(G_OBJECT(Notif));
notify_uninit();
}

View File

@ -4,20 +4,46 @@ Fonctions utilitaires concernant les tâches
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include <libnotify/notify.h> #include <string.h>
#include "include/db.h" #include "include/db.h"
#include "include/struct.h" #include "include/struct.h"
#include "include/colors.h" #include "include/colors.h"
void parse_input(char* in_text, char** out_text, char** out_category) {
int n = strlen(in_text);
int i=0;
for (;i < n; i++) {
if (in_text[i] == ':') {
break;
}
}
if (i == n) {
*out_text = in_text;
*out_category = NULL;
} else {
*out_text = malloc(sizeof(char)*(n-i));
*out_category = malloc(sizeof(char)*i);
memcpy(*out_text, (void*)in_text+((i+1)*sizeof(char)), (size_t)((n-i)*sizeof(char)));
memcpy(*out_category, in_text, (size_t)(i*sizeof(char)));
(*out_category)[i] = '\0';
}
}
task_t create_task(char* text, time_t due_to) { task_t create_task(char* text, time_t due_to) {
task_t task; task_t task;
task.id = get_new_task_id(); task.id = get_new_task_id();
task.text = text;
task.done = false; task.done = false;
task.due_to = due_to; task.due_to = due_to;
task.text = NULL;
task.category = NULL;
parse_input(text, &(task.text), &(task.category));
return task; return task;
} }
@ -25,6 +51,9 @@ task_t create_task(char* text, time_t due_to) {
void print_task(task_t task) { void print_task(task_t task) {
printf(YELLOW "=== Tâche " BOLD "[%d]" RESET YELLOW " ===\n" RESET, task.id); printf(YELLOW "=== Tâche " BOLD "[%d]" RESET YELLOW " ===\n" RESET, task.id);
if (task.category) {
printf(BLUE "%s:" RESET, task.category);
}
printf(BOLD "%s\n" RESET, task.text); printf(BOLD "%s\n" RESET, task.text);
printf("Statut: "); printf("Statut: ");
@ -53,13 +82,21 @@ void print_task(task_t task) {
} }
void print_task_list(task_list_t* list, bool show_completed) { void print_single_task(task_t t, bool show_category, char* color) {
if (t.category && show_category) {
printf(BOLD "%s[%d]" RESET " " BLUE "%s:" RESET "%s\n", color, t.id, t.category, t.text);
} else {
printf(BOLD "%s[%d]" RESET " %s\n", color, t.id, t.text);
}
}
void print_task_list(task_list_t* list, bool show_category, bool show_completed) {
task_list_t* cur = list; task_list_t* cur = list;
time_t now = time(0); time_t now = time(0);
while (cur) { // Show not completed red tasks first while (cur) { // Show not completed red tasks first
task_t t = cur->task; task_t t = cur->task;
if (!t.done && difftime(now, t.due_to) >= 0 && t.due_to != 0) { if (!t.done && difftime(now, t.due_to) >= 0 && t.due_to != 0) {
printf(BOLD RED "[%d]" RESET " %s\n", t.id, t.text); // Task not completed but should be ! print_single_task(t, show_category, RED); // Task not completed but should be !
} }
cur = cur->next; cur = cur->next;
} }
@ -68,7 +105,7 @@ void print_task_list(task_list_t* list, bool show_completed) {
while (cur) { // Show not completed but not due_to now then while (cur) { // Show not completed but not due_to now then
task_t t = cur->task; task_t t = cur->task;
if (!t.done && (difftime(now, t.due_to) <= 0 || t.due_to == 0)) { if (!t.done && (difftime(now, t.due_to) <= 0 || t.due_to == 0)) {
printf(BOLD YELLOW "[%d]" RESET " %s\n", t.id, t.text); // Task not completed but fine print_single_task(t, show_category, YELLOW); // Task not completed but fine
} }
cur = cur->next; cur = cur->next;
} }
@ -78,7 +115,7 @@ void print_task_list(task_list_t* list, bool show_completed) {
while (cur) { // Show completed while (cur) { // Show completed
task_t t = cur->task; task_t t = cur->task;
if (t.done) { if (t.done) {
printf(BOLD GREEN "[%d]" RESET " %s\n", t.id, t.text); // Task already done print_single_task(t, show_category, GREEN); // Task already done
} }
cur = cur->next; cur = cur->next;
} }
@ -93,12 +130,4 @@ void free_task_list(task_list_t* list) {
free(list); free(list);
list = next; list = next;
} }
}
void desktop_notification(task_t t) {
notify_init ("TaKl");
NotifyNotification * Notif = notify_notification_new ("TaKl Daemon", t.text, "dialog-information");
notify_notification_show (Notif, NULL);
g_object_unref(G_OBJECT(Notif));
notify_uninit();
} }

View File

@ -8,22 +8,30 @@
#include "include/struct.h" #include "include/struct.h"
char* get_socket_path() { char* get_socket_path() {
#ifndef SOCKET_FILE /*
char* base_path = "/tmp/takl"; Checks if $TAKL_SOCKET env variable is set
else, the socket is located at /tmp/takl
*/
char* env_socket_path = getenv("TAKL_SOCKET");
if (env_socket_path) {
char* socket_path = malloc(sizeof(char)*(strlen(env_socket_path)+1));
strcpy(socket_path, env_socket_path);
return socket_path;
} else {
char* base_path = TMPDIR "/takl";
char* username = getenv("USER"); char* username = getenv("USER");
if (!username)
username = getenv("USERNAME");
assert(username != NULL); assert(username != NULL);
char* socket_path = malloc(sizeof(char)*(strlen(base_path)+strlen(username)+1)); char* socket_path = malloc(sizeof(char)*(strlen(base_path)+strlen(username)+1));
sprintf(socket_path, "%s.%s", base_path, username); sprintf(socket_path, "%s.%s", base_path, username);
return socket_path; return socket_path;
#else }
char* socket_path = malloc(sizeof(char)*(strlen(SOCKET_FILE)+1));
memcpy(socket_path, SOCKET_FILE, sizeof(char)*(strlen(SOCKET_FILE)+1));
return socket_path;
#endif
} }

42
tests/main.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash
TMPDIR=$(mktemp -d)
#! On ne teste pas que l'on peut bien récupérer ces chemins automatiquement,
#! ce qui peut-être une source d'erreurs
export TAKL_DB="$TMPDIR/takl.sqlite3"
export TAKL_SOCKET="$TMPDIR/takl.socket"
echo "Using $TMPDIR"
make -j
# Catégories
./takl add "nocategory"
./takl add "category1:test1"
# Dates
./takl add "date:avec date" min+5
./takl add "date:avec date2" h+5
./takl add "date:avec date3" j+5
./takl add "date:avec date4" "31/12"
TASK_ID=$(./takl add "done soon" | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' | awk '-F[' '{ print $2 }' | awk '-F]' '{ print $1 }')
# List
./takl list
./takl list date
# Reschedule
./takl reschedule $TASK_ID "22/11"
# Get info
./takl info $TASK_ID
# Mark as done
./takl done $TASK_ID
./takl list -a
./takl rm $TASK_ID
#* À rajouter: tests sur le fait que les changements ont bien eu lieu
rm $TMPDIR -r