diff --git a/src/note.c b/src/note.c index 7fc8f58..9a724dd 100644 --- a/src/note.c +++ b/src/note.c @@ -3,7 +3,12 @@ #include #include "pit.h" +void pit_note_delete(ulong id, PTask pt) +{ + +} + void pit_note(char *argv[]) { puts("pit: note is not implemented yet"); -} \ No newline at end of file +} diff --git a/src/pit.h b/src/pit.h index 93631ea..cdb0c98 100644 --- a/src/pit.h +++ b/src/pit.h @@ -53,6 +53,9 @@ void pit_db_load(); void pit_db_save(); void pit_db_initialize(); void pit_action(ulong id, char *subject, char *message); +void pit_task_delete(ulong id, PProject pp); +void pit_note_delete(ulong id, PTask pt); + /* Argument parsing helpers */ int pit_arg_is_option(char **arg); @@ -65,6 +68,7 @@ time_t pit_arg_time(char **arg, char *required); /* Misc utilities */ void die(char *msg, ...); void perish(char *prefix); +char *str2str(char *str); char *mem2str(char *mem, int len); char *current_user(); char *home_dir(char *username, int len); diff --git a/src/project.c b/src/project.c index 0099a87..d8c27a1 100644 --- a/src/project.c +++ b/src/project.c @@ -3,20 +3,36 @@ #include #include "pit.h" -static int already_exist(char *name) -{ - PProject pp; +static void project_list(char *name, char *status); +static void project_show(ulong id); +static void project_create(char *name, char *status); +static void project_update(ulong id, char *name, char *status); +static void project_delete(ulong id); +static bool project_already_exist(char *name); +static void project_find_current(PProject *ppp, ulong *pid); +static void project_log_create(PProject pp, char *name, char *status); +static void project_log_update(PProject pp, char *name, char *status); +static void project_log_delete(ulong id, char *name, ulong number_of_tasks); +static void project_parse_options(char **arg, char **name, char **status); - pit_db_load(); - for_each_project(pp) { - if (!strcmp(pp->name, name)) { - return 1; - } - } - return 0; -} +/* +** CREATING PROJECTS: +** pit project -c name [-s status] +** +** EDITING PROJECTS: +** pit project -e [number] [-n name] [-s status] +** +** DELETING PROJECTS: +** pit project -d [number] +** +** VIEWING PROJECT: +** pit project [[-q] number] +** +** LISTING PROJECTS: +** pit project -q [number | [-n name] [-s status]] +*/ -static void list_projects() +static void project_list(char *name, char *status) { PProject pp; PPager ppager; @@ -31,11 +47,11 @@ static void list_projects() } } -static void create_project(char *name, char *status) +static void project_create(char *name, char *status) { pit_db_load(); - if (already_exist(name)) { + if (project_already_exist(name)) { die("project with the same name already exists"); } else { Project p, *pp; @@ -55,12 +71,12 @@ static void create_project(char *name, char *status) } } -static int show_project(ulong number) +static void project_show(ulong id) { PProject pp; pit_db_load(); - pp = (PProject)pit_table_find(projects, number); + pp = (PProject)pit_table_find(projects, id); if (pp) { printf("%lu: %s (%s, %lu task%s)\n", pp->id, pp->name, pp->status, pp->number_of_tasks, (pp->number_of_tasks != 1 ? "s" : "")); @@ -77,56 +93,192 @@ static int show_project(ulong number) } else { die("could not find the project"); } - return 1; } -static int delete_project(unsigned long number) +static void project_update(ulong id, char *name, char *status) { PProject pp; - printf("deleting project %lu\n", number); pit_db_load(); - pp = (PProject)pit_table_delete(projects, number); + project_find_current(&pp, &id); + + if (name) strncpy(pp->name, name, sizeof(pp->name) - 1); + if (status) strncpy(pp->status, status, sizeof(pp->status) - 1); + pit_table_mark(projects, pp->id); + + project_log_update(pp, name, status); + pit_db_save(); +} + +static void project_delete(ulong id) +{ + PProject pp; + PTask pt; + char *deleted_name; + ulong deleted_number_of_tasks; + + pit_db_load(); + project_find_current(&pp, &id); + + /* + ** Delete project tasks. + */ + if (pp->number_of_tasks > 0) { + for_each_task(pt) { + if (pt->project_id == id) { + pit_task_delete(pt->id, pp); + --pt; /* Make the task pointer stay since it now points to the next task. */ + } + } + } + /* + ** Ready to delete the project itself. But first preserve the + ** name and number of tasks since we need these bits for logging. + */ + deleted_name = str2str(pp->name); + deleted_number_of_tasks = pp->number_of_tasks; + pp = (PProject)pit_table_delete(projects, id); if (pp) { - pit_table_mark(projects, 0); + pit_table_mark(projects, 0); /* TODO: find better current project candidate. */ + project_log_delete(id, deleted_name, deleted_number_of_tasks); pit_db_save(); } else { die("could not delete the project"); } - return 1; +} + +static bool project_already_exist(char *name) +{ + PProject pp; + + pit_db_load(); + for_each_project(pp) { + if (!strcmp(pp->name, name)) { + return TRUE; + } + } + return FALSE; +} + +static void project_find_current(PProject *ppp, ulong *pid) +{ + if (*pid) { + *ppp = (PProject)pit_table_find(projects, *pid); + if (!*ppp) die("could not find project %lu", *pid); + } else { + *ppp = (PProject)pit_table_current(projects); + if (!*ppp) die("could not find current project"); + else *pid = (*(PProject *)ppp)->id; + } +} + +static void project_log_create(PProject pp, char *name, char *status) +{ + char str[256]; + + sprintf(str, "created project %lu: %s (status: %s)", pp->id, name, status); + puts(str); + pit_action(pp->id, "project", str); +} + +static void project_log_update(PProject pp, char *name, char *status) +{ + char str[256]; + bool empty = TRUE; + + sprintf(str, "updated project %lu:", pp->id); + if (name) { + sprintf(str + strlen(str), " (name: %s", name); + empty = FALSE; + } else { + sprintf(str + strlen(str), " %s (", pp->name); + } + if (status) { + sprintf(str + strlen(str), "%sstatus: %s)", (empty ? "" : ", "), status); + } + puts(str); + pit_action(pp->id, "project", str); +} + +static void project_log_delete(ulong id, char *name, ulong number_of_tasks) +{ + char str[256]; + + sprintf(str, "deleted project %lu: %s", id, name); + if (number_of_tasks > 0) { + sprintf(str + strlen(str), " with %lu task%s", number_of_tasks, (number_of_tasks == 1 ? "" : "s")); + } + puts(str); + pit_action(id, "project", str); +} + +static void project_parse_options(char **arg, char **name, char **status) +{ + while(*++arg) { + switch(pit_arg_option(arg)) { + case 's': + *status = pit_arg_string(++arg, "project status"); + break; + case 'n': + if (name) { + *name = pit_arg_string(++arg, "project name"); + break; + } /* else fall though */ + default: + die("invalid project option: %s", *arg); + } + } } void pit_project(char *argv[]) { char **arg = &argv[1]; - unsigned long number; + char *name = NULL, *status = NULL; + ulong number = 0L; if (!*arg) { - list_projects(); - } else if (!strcmp(*arg, "-c")) { - if (!*++arg) { - die("missing project name"); + project_list(name, status); /* Show all projects. */ + } else { /* pit project [number] */ + number = pit_arg_number(arg, NULL); + if (number) { + project_show(number); } else { - create_project(*arg, *(arg + 1)); - } - } else if (!strcmp(*arg, "-d")) { - if (!*++arg) { - die("missing project number"); - } else { - number = atoi(*arg); - if (!number) { - die("invalid project number"); - } else { - delete_project(number); + switch(pit_arg_option(arg)) { + case 'c': /* pit project -c name [-s status] */ + name = pit_arg_string(++arg, "project name"); + project_parse_options(arg, NULL, &status); + project_create(name, status); + break; + case 'e': /* pit project -e [number] [-n name] [-s status] */ + number = pit_arg_number(++arg, NULL); + if (!number) --arg; + project_parse_options(arg, &name, &status); + if (!name && !status) { + die("nothing to update"); + } else { + project_update(number, name, status); + } + break; + case 'd': /* pit project -d [number] */ + number = pit_arg_number(++arg, NULL); + project_delete(number); + break; + case 'q': /* pit project -q [number | [-n name] [-s status]] */ + number = pit_arg_number(++arg, NULL); + if (number) { + project_show(number); + } else { + project_parse_options(--arg, &name, &status); + if (!name && !status) { + project_show(0); /* Show current project if any. */ + } else { + project_list(name, status); + } + } + break; + default: + die("invalid project option: %s", *arg); } } - /* } else if (!strcmp(*arg, "-e")) { TODO: Edit */ - } else { - number = atoi(*arg); - if (!number) { - die("invalid project parameters"); - } else { - show_project(number); - } } } diff --git a/src/table.c b/src/table.c index 19af51c..84c2461 100644 --- a/src/table.c +++ b/src/table.c @@ -107,7 +107,7 @@ uchar *pit_table_find(PTable pt, ulong id) { ** if the record was not found. */ uchar *pit_table_delete(PTable pt, ulong id) { - // TODO: retrn NULL (or raise?) if table doesn't have id. + // TODO: return NULL (or die?) if table doesn't have id. register uchar *pr = (uchar *)pit_table_find(pt, id); if (pr) { diff --git a/src/task.c b/src/task.c index 43e414b..1011385 100644 --- a/src/task.c +++ b/src/task.c @@ -4,13 +4,13 @@ #include "pit.h" static void task_list(char *name, char *status, char *priority, time_t date, time_t time); -static void task_show(ulong number); +static void task_show(ulong id); static void task_create(char *name, char *status, char *priority, time_t date, time_t time); -static void task_update(ulong number, char *name, char *status, char *priority, time_t date, time_t time); -static void task_delete(ulong number); +static void task_update(ulong id, char *name, char *status, char *priority, time_t date, time_t time); +static void task_find_current(PTask *ppt, ulong *pid); static void task_log_create(PTask pt, char *name, char *status, char *priority, time_t date, time_t time); static void task_log_update(PTask pt, char *name, char *status, char *priority, time_t date, time_t time); -static void task_log_delete(PTask pt); +static void task_log_delete(ulong id, char *name, ulong number_of_notes); static void task_parse_options(char **arg, char **name, char **status, char **priority, time_t *date, time_t *time); /* @@ -27,7 +27,7 @@ static void task_parse_options(char **arg, char **name, char **status, char **pr ** pit task [[-q] number] ** ** LISTING TASKS: -** pit task -q [-s status] [-p priority] [-d date] [-t time] +** pit task -q [number | [-n name] [-s status] [-p priority] [-d date] [-t time]] */ static void task_list(char *name, char *status, char *priority, time_t date, time_t time) @@ -50,9 +50,9 @@ static void task_list(char *name, char *status, char *priority, time_t date, tim } } -static void task_show(ulong number) +static void task_show(ulong id) { - printf("task_show(%lu)\n", number); + printf("task_show(%lu)\n", id); } static void task_create(char *name, char *status, char *priority, time_t date, time_t time) @@ -87,18 +87,13 @@ static void task_create(char *name, char *status, char *priority, time_t date, t } } -static void task_update(ulong number, char *name, char *status, char *priority, time_t date, time_t time) +static void task_update(ulong id, char *name, char *status, char *priority, time_t date, time_t time) { PTask pt; pit_db_load(); - if (number) { - pt = (PTask)pit_table_find(tasks, number); - if (!pt) die("could not find task %lu", number); - } else { - pt = (PTask)pit_table_current(tasks); - if (!pt) die("could not find current task"); - } + task_find_current(&pt, &id); + if (name) strncpy(pt->name, name, sizeof(pt->name) - 1); if (status) strncpy(pt->status, status, sizeof(pt->status) - 1); if (priority) strncpy(pt->priority, priority, sizeof(pt->priority) - 1); @@ -110,26 +105,68 @@ static void task_update(ulong number, char *name, char *status, char *priority, pit_db_save(); } -static void task_delete(ulong number) +/* +** A task could be deleted as standalone entity or as part of cascading project +** delete. In later case we're going to have 'pp' set and the database loaded. +*/ +void pit_task_delete(ulong id, PProject pp) { PTask pt; - PNote pn; + bool standalone = (pp == NULL); - pit_db_load(); - pt = (PTask)pit_table_delete(tasks, number); - if (pt) { - pit_table_mark(tasks, 0); + if (standalone) { + pit_db_load(); + pp = (PProject)pit_table_find(projects, pt->project_id); + } + task_find_current(&pt, &id); + + if (pp) { + /* + ** First delete task notes if any. + */ if (pt->number_of_notes > 0) { + PNote pn; for_each_note(pn) { - if (pn->task_id == pt->id) { - pit_table_delete(notes, pn->id); + if (pn->task_id == id) { + pit_note_delete(pn->id, pt); + --pn; /* Make the note pointer stay since it now points to the next note. */ } } } - task_log_delete(pt); - pit_db_save(); + /* + ** Preserve task name and number_of_notes before deleting the task since + ** we need these for logging. + */ + char *deleted_name = str2str(pt->name); + ulong deleted_number_of_notes = pt->number_of_notes; + + pt = (PTask)pit_table_delete(tasks, id); + if (pt) { + pit_table_mark(tasks, 0); /* TODO: find better current task candidate. */ + task_log_delete(id, deleted_name, deleted_number_of_notes); + if (standalone) { + pp->number_of_tasks--; + pit_db_save(); + } + free(deleted_name); + } else { + free(deleted_name); + die("could not delete task %lu", id); + } } else { - die("could not delete task %lu", number); + die("could not find project for task %lu", id); + } +} + +static void task_find_current(PTask *ppt, ulong *pid) +{ + if (*pid) { + *ppt = (PTask)pit_table_find(tasks, *pid); + if (!*ppt) die("could not find task %lu", *pid); + } else { + *ppt = (PTask)pit_table_current(tasks); + if (!*ppt) die("could not find current task"); + else *pid = (*(PTask *)ppt)->id; } } @@ -175,16 +212,16 @@ static void task_log_update(PTask pt, char *name, char *status, char *priority, pit_action(pt->id, "task", str); } -static void task_log_delete(PTask pt) +static void task_log_delete(ulong id, char *name, ulong number_of_notes) { char str[256]; - sprintf(str, "deleted task %lu: %s", pt->id, pt->name); - if (pt->number_of_notes > 0) { - sprintf(str + strlen(str), " with %lu note%s", pt->number_of_notes, (pt->number_of_notes == 1 ? "" : "s")); + sprintf(str, "deleted task %lu: %s", id, name); + if (number_of_notes > 0) { + sprintf(str + strlen(str), " with %lu note%s", number_of_notes, (number_of_notes == 1 ? "" : "s")); } puts(str); - pit_action(pt->id, "task", str); + pit_action(id, "task", str); } static void task_parse_options(char **arg, char **name, char **status, char **priority, time_t *date, time_t *time) @@ -223,7 +260,7 @@ void pit_task(char *argv[]) ulong number = 0L; if (!*arg) { - task_list(NULL, NULL, NULL, 0, 0); /* List all tasks (with default paramaters). */ + task_list(name, status, priority, date, time); /* List all tasks (i.e. use default paramaters). */ } else { /* pit task [number] */ number = pit_arg_number(arg, NULL); if (number) { @@ -235,7 +272,7 @@ void pit_task(char *argv[]) task_parse_options(arg, NULL, &status, &priority, &date, &time); task_create(name, status, priority, date, time); break; - case 'e': /* pit task -e [number] [-n name] [-s status] [-p priority] [-d date] [-t time]*/ + case 'e': /* pit task -e [number] [-n name] [-s status] [-p priority] [-d date] [-t time] */ number = pit_arg_number(++arg, NULL); if (!number) --arg; task_parse_options(arg, &name, &status, &priority, &date, &time); @@ -247,7 +284,7 @@ void pit_task(char *argv[]) break; case 'd': /* pit task -d [number] */ number = pit_arg_number(++arg, NULL); - task_delete(number); + pit_task_delete(number, NULL); /* Delete the task, but keep its project. */ break; case 'q': /* pit task -q [number | [-n name] [-s status] [-p priority] [-d date] [-t time]] */ number = pit_arg_number(++arg, NULL); @@ -256,7 +293,7 @@ void pit_task(char *argv[]) } else { task_parse_options(--arg, &name, &status, &priority, &date, &time); if (!name && !status && !priority && !date && !time) { - task_show(0); + task_show(0); /* Show current task if any. */ } else { task_list(name, status, priority, date, time); } diff --git a/src/util.c b/src/util.c index 81564c8..c6a0fe3 100644 --- a/src/util.c +++ b/src/util.c @@ -6,6 +6,10 @@ #include #include "pit.h" +char *str2str(char *str) { + return strcpy(malloc(strlen(str) + 1), str); /* Cheap strdup() */ +} + char *mem2str(char *mem, int len) { char *str = malloc(len + 1); memcpy(str, mem, len);