avoid removal of recurrent task due to TW-638 bug of taskwarrior
[ptask.git] / src / tw.c
index ae67094..3049c0b 100644 (file)
--- a/src/tw.c
+++ b/src/tw.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 jeanfi@gmail.com
+ * Copyright (C) 2012-2013 jeanfi@gmail.com
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301 USA
  */
+#define _GNU_SOURCE
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <time.h>
 
+#include <json.h>
 
-char *task_exec(char *opts)
+#include <log.h>
+#include "note.h"
+#include <pstr.h>
+#include "tw.h"
+
+struct tm *parse_time(const char *t)
+{
+       struct tm *tm;
+
+       tm = malloc(sizeof(struct tm));
+       memset(tm, 0, sizeof(struct tm));
+       strptime(t, "%Y%m%dT%H%M%S%Z", tm);
+
+       return tm;
+}
+
+static char *task_exec(char *opts)
 {
        FILE *f;
        int ret;
        size_t s;
        char *str, *tmp, *cmd, buf[1024];
 
-       cmd = malloc(strlen("task rc.json.array=on ") + strlen(opts) + 1);
-       strcpy(cmd, "task rc.json.array=on ");
+       log_fct_enter();
+
+       cmd = malloc(strlen("task ") + strlen(opts) + 1);
+       strcpy(cmd, "task ");
        strcat(cmd, opts);
 
-       printf("execute: %s\n", cmd);
+       log_debug("execute: %s", cmd);
 
        f = popen(cmd, "r");
 
+       free(cmd);
+
        if (!f) {
                perror("popen");
-               str = NULL;
-               goto exit_free;
+               return NULL;
        }
 
        str = strdup("");
@@ -55,35 +78,87 @@ char *task_exec(char *opts)
 
        ret = pclose(f);
 
-       if (ret == -1) {
-               printf("pclose fails\n");
-               perror("pclose");
-       }
+       if (ret == -1)
+               log_err("pclose fails");
 
- exit_free:
-       free(cmd);
+       log_fct_exit();
 
        return str;
 }
 
-#include <json/json.h>
+static char *task_get_version()
+{
+       char *out;
 
-#include "tw.h"
+       out = task_exec("--version");
+
+       trim(out);
+
+       return out;
+}
+
+static int task_check_version()
+{
+       char *ver;
+
+       ver = task_get_version();
+
+       if (!ver)
+               return 0;
+
+       log_debug("task version: %s", ver);
+
+       if (!strcmp(ver, "2.2.0")
+           || !strcmp(ver, "2.0.0")
+           || !strcmp(ver, "2.3.0"))
+               return 1;
+       else
+               return 0;
+}
+
+static char *tw_exec(char *opts)
+{
+       char *opts2;
+
+       if (!task_check_version()) {
+               log_err("ptask is not compatible with the installed version of"
+                       " taskwarrior.");
+               return NULL;
+       }
+
+       opts2 = malloc(strlen("rc.confirmation:no ")
+                      + strlen(opts)
+                      + 1);
+       strcpy(opts2, "rc.confirmation:no ");
+       strcat(opts2, opts);
+
+       return task_exec(opts2);
+}
 
-static struct json_object *task_exec_json(char *opts)
+static struct json_object *task_exec_json(const char *opts)
 {
        struct json_object *o;
-       char *str;
+       char *str, *cmd;
+
+       cmd = malloc(strlen("rc.json.array=on ") + strlen(opts) + 1);
+       strcpy(cmd, "rc.json.array=on ");
+       strcat(cmd, opts);
 
-       str = task_exec(opts);
+       str = tw_exec(cmd);
 
        if (str) {
                o = json_tokener_parse(str);
                free(str);
-               return o;
+       } else {
+               o = NULL;
        }
 
-       return NULL;
+       free(cmd);
+
+       if (o && is_error(o))
+               return NULL;
+
+       return o;
 }
 
 struct task **tw_get_all_tasks(const char *status)
@@ -92,9 +167,12 @@ struct task **tw_get_all_tasks(const char *status)
        struct json_object *jtasks, *jtask, *json;
        struct task **tasks;
        char *opts;
+       const char *urg;
 
        opts = malloc(strlen("export status:") + strlen(status) + 1);
-       sprintf(opts, "export status:%s", status);
+
+       strcpy(opts, "export status:");
+       strcat(opts, status);
 
        jtasks = task_exec_json(opts);
        free(opts);
@@ -125,12 +203,49 @@ struct task **tw_get_all_tasks(const char *status)
                        tasks[i]->project
                                = strdup(json_object_get_string(json));
                else
-                       tasks[i]->project = NULL;
+                       tasks[i]->project = strdup("");
+
+               json = json_object_object_get(jtask, "priority");
+               if (json)
+                       tasks[i]->priority
+                               = strdup(json_object_get_string(json));
+               else
+                       tasks[i]->priority = strdup("");
 
                json = json_object_object_get(jtask, "uuid");
                tasks[i]->uuid = strdup(json_object_get_string(json));
 
-               tasks[i]->note = NULL;
+               json = json_object_object_get(jtask, "urgency");
+               urg = json_object_get_string(json);
+               if (urg)
+                       tasks[i]->urgency = strdup(urg);
+               else
+                       tasks[i]->urgency = NULL;
+
+               tasks[i]->note = note_get(tasks[i]->uuid);
+
+               json = json_object_object_get(jtask, "entry");
+               tasks[i]->entry = parse_time(json_object_get_string(json));
+
+               json = json_object_object_get(jtask, "due");
+               if (json)
+                       tasks[i]->due
+                               = parse_time(json_object_get_string(json));
+               else
+                       tasks[i]->due = NULL;
+
+               json = json_object_object_get(jtask, "start");
+               if (json)
+                       tasks[i]->start
+                               = parse_time(json_object_get_string(json));
+               else
+                       tasks[i]->start = NULL;
+
+               json = json_object_object_get(jtask, "recur");
+               if (json)
+                       tasks[i]->recur = strdup(json_object_get_string(json));
+               else
+                       tasks[i]->recur = NULL;
        }
 
        tasks[n] = NULL;
@@ -140,7 +255,7 @@ struct task **tw_get_all_tasks(const char *status)
        return tasks;
 }
 
-char *escape(const char *txt)
+static char *escape(const char *txt)
 {
        char *result;
        char *c;
@@ -151,16 +266,12 @@ char *escape(const char *txt)
        while (*txt) {
                switch (*txt) {
                case '"':
-                       *c = '\\'; c++;
-                       *c = '"';
-                       break;
                case '$':
-                       *c = '\\'; c++;
-                       *c = '$';
-                       break;
                case '&':
+               case '<':
+               case '>':
                        *c = '\\'; c++;
-                       *c = '&';
+                       *c = *txt;
                        break;
                default:
                        *c = *txt;
@@ -176,22 +287,18 @@ char *escape(const char *txt)
 
 void tw_modify_description(const char *uuid, const char *newdesc)
 {
-       char *str;
        char *opts;
 
-       str = escape(newdesc);
-
        opts = malloc(1
                      + strlen(uuid)
                      + strlen(" modify :\"")
-                     + strlen(str)
+                     + strlen(newdesc)
                      + strlen("\"")
                      + 1);
-       sprintf(opts, " %s modify \"%s\"", uuid, str);
+       sprintf(opts, " %s modify \"%s\"", uuid, newdesc);
 
-       task_exec(opts);
+       tw_exec(opts);
 
-       free(str);
        free(opts);
 }
 
@@ -210,8 +317,283 @@ void tw_modify_project(const char *uuid, const char *newproject)
                      + 1);
        sprintf(opts, " %s modify project:\"%s\"", uuid, str);
 
-       task_exec(opts);
+       tw_exec(opts);
 
        free(str);
        free(opts);
 }
+
+void tw_modify_priority(const char *uuid, const char *priority)
+{
+       char *str;
+       char *opts;
+
+       log_fct_enter();
+
+       str = escape(priority);
+
+       opts = malloc(1
+                     + strlen(uuid)
+                     + strlen(" modify priority:\"")
+                     + strlen(str)
+                     + strlen("\"")
+                     + 1);
+       sprintf(opts, " %s modify priority:\"%s\"", uuid, str);
+
+       tw_exec(opts);
+
+       free(str);
+       free(opts);
+
+       log_fct_exit();
+}
+
+void tw_add(const char *newdesc, const char *prj, const char *prio)
+{
+       char *opts, *eprj;
+
+       log_fct_enter();
+
+       eprj = escape(prj);
+
+       opts = malloc(strlen("add")
+                     + strlen(" priority:")
+                     + 1
+                     + strlen(" project:\\\"")
+                     + strlen(eprj)
+                     + strlen("\\\"")
+                     + strlen(" \"")
+                     + strlen(newdesc)
+                     + strlen("\"")
+                     + 1);
+
+       strcpy(opts, "add");
+
+       if (prio && strlen(prio) == 1) {
+               strcat(opts, " priority:");
+               strcat(opts, prio);
+       }
+
+       if (eprj && strlen(prj)) {
+               strcat(opts, " project:\\\"");
+               strcat(opts, eprj);
+               strcat(opts, "\\\"");
+       }
+
+       strcat(opts, " \"");
+       strcat(opts, newdesc);
+       strcat(opts, "\"");
+
+       tw_exec(opts);
+
+       free(opts);
+       free(eprj);
+
+       log_fct_exit();
+}
+
+void tw_task_done(const char *uuid)
+{
+       char *opts;
+
+       opts = malloc(1
+                     + strlen(uuid)
+                     + strlen(" done")
+                     + 1);
+       sprintf(opts, " %s done", uuid);
+
+       tw_exec(opts);
+
+       free(opts);
+}
+
+void tw_task_start(const char *uuid)
+{
+       char *opts;
+
+       opts = malloc(1
+                     + strlen(uuid)
+                     + strlen(" start")
+                     + 1);
+       sprintf(opts, " %s start", uuid);
+
+       tw_exec(opts);
+
+       free(opts);
+}
+
+void tw_task_stop(const char *uuid)
+{
+       char *opts;
+
+       opts = malloc(1
+                     + strlen(uuid)
+                     + strlen(" stop")
+                     + 1);
+       sprintf(opts, " %s stop", uuid);
+
+       tw_exec(opts);
+
+       free(opts);
+}
+
+void tw_task_remove(const char *uuid)
+{
+       char *opts;
+
+       opts = malloc(1
+                     + strlen(uuid)
+                     + strlen(" delete")
+                     + 1);
+       sprintf(opts, " %s delete", uuid);
+
+       tw_exec(opts);
+
+       free(opts);
+}
+
+static void task_free(struct task *task)
+{
+       if (!task)
+               return ;
+
+       free(task->description);
+       free(task->status);
+       free(task->uuid);
+       free(task->note);
+       free(task->project);
+       free(task->priority);
+       free(task->urgency);
+       free(task->entry);
+       free(task->due);
+       free(task->start);
+       free(task->recur);
+
+       free(task);
+}
+
+void tw_task_list_free(struct task **tasks)
+{
+       struct task **cur;
+
+       if (!tasks)
+               return ;
+
+       for (cur = tasks; *cur; cur++)
+               task_free(*cur);
+
+       free(tasks);
+}
+
+static void project_free(struct project *p)
+{
+       if (!p)
+               return ;
+
+       free(p->name);
+       free(p);
+}
+
+void tw_project_list_free(struct project **prjs)
+{
+       struct project **cur;
+
+       if (!prjs)
+               return ;
+
+       for (cur = prjs; *cur; cur++)
+               project_free(*cur);
+
+       free(prjs);
+}
+
+static struct project *project_list_get(struct project **prjs, const char *name)
+{
+       struct project **cur;
+
+       for (cur = prjs; *cur; cur++)
+               if (!strcmp((*cur)->name, name))
+                       return *cur;
+
+       return NULL;
+}
+
+static struct project *project_new(const char *name, int count)
+{
+       struct project *prj;
+
+       prj = malloc(sizeof(struct project));
+
+       prj->name = strdup(name);
+       prj->count = count;
+
+       return prj;
+}
+
+static int projects_length(struct project **list)
+{
+       int n;
+
+       if (!list)
+               return 0;
+
+       n = 0;
+       while (*list) {
+               n++;
+               list++;
+       }
+
+       return n;
+}
+
+static struct project **projects_add(struct project **list, void *item)
+{
+       int n;
+       struct project **result;
+
+       n = projects_length(list);
+
+       result = (struct project **)malloc
+               ((n + 1 + 1) * sizeof(struct project *));
+
+       if (list)
+               memcpy(result, list, n * sizeof(struct project *));
+
+       result[n] = item;
+       result[n + 1] = NULL;
+
+       return result;
+}
+
+struct project **tw_get_projects(struct task **tasks)
+{
+       struct task **t_cur;
+       struct project **prjs, **tmp, *prj;
+       const char *prj_name;
+
+       log_fct_enter();
+
+       prjs = malloc(2 * sizeof(struct project *));
+       prjs[0] = project_new("ALL", 0);
+       prjs[1] = NULL;
+
+       for (t_cur = tasks; *t_cur; t_cur++) {
+               prj_name = (*t_cur)->project;
+               prj = project_list_get(prjs, prj_name);
+               if (prj) {
+                       prj->count++;
+               } else {
+                       prj = project_new(prj_name, 1);
+
+                       tmp = projects_add(prjs, prj);
+
+                       free(prjs);
+                       prjs = tmp;
+               }
+               prjs[0]->count++;
+       }
+
+       log_fct_exit();
+
+       return prjs;
+}