version check has been inversed by error...
[ptask.git] / src / tw.c
1 /*
2  * Copyright (C) 2012-2013 jeanfi@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA
18  */
19 #define _GNU_SOURCE
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <time.h>
26
27 #include <json.h>
28
29 #include <log.h>
30 #include "note.h"
31 #include <pstr.h>
32 #include "tw.h"
33
34 /* Whether ptask check that the taskwarrior version is supported. */
35 static int check_version_enabled = 1;
36
37 struct tm *parse_time(const char *t)
38 {
39         struct tm *tm;
40
41         tm = malloc(sizeof(struct tm));
42         memset(tm, 0, sizeof(struct tm));
43         strptime(t, "%Y%m%dT%H%M%S%Z", tm);
44
45         return tm;
46 }
47
48 static char *task_exec(char *opts)
49 {
50         FILE *f;
51         int ret;
52         size_t s;
53         char *str, *tmp, *cmd, buf[1024];
54
55         log_fct_enter();
56
57         cmd = malloc(strlen("task ") + strlen(opts) + 1);
58         strcpy(cmd, "task ");
59         strcat(cmd, opts);
60
61         log_debug("execute: %s", cmd);
62
63         f = popen(cmd, "r");
64
65         free(cmd);
66
67         if (!f) {
68                 perror("popen");
69                 return NULL;
70         }
71
72         str = strdup("");
73         while ((s = fread(buf, 1, 1024, f))) {
74                 tmp = malloc(strlen(str) + s + (size_t)1);
75                 memcpy(tmp, str, strlen(str));
76                 memcpy(tmp + strlen(str), buf, s);
77                 tmp[strlen(str) + s] = '\0';
78                 free(str);
79                 str = tmp;
80         }
81
82         ret = pclose(f);
83
84         if (ret == -1)
85                 log_err("pclose fails");
86
87         log_fct_exit();
88
89         return str;
90 }
91
92 static char *task_get_version()
93 {
94         char *out;
95
96         out = task_exec("--version");
97
98         trim(out);
99
100         return out;
101 }
102
103 static int task_check_version()
104 {
105         char *ver;
106
107         ver = task_get_version();
108
109         if (!ver)
110                 return 0;
111
112         log_debug("task version: %s", ver);
113
114         if (!strcmp(ver, "2.2.0")
115             || !strcmp(ver, "2.0.0")
116             || !strcmp(ver, "2.3.0")
117             || !strcmp(ver, "2.4.0")
118             || !strcmp(ver, "2.4.1"))
119                 return 1;
120         else
121                 return 0;
122 }
123
124 static char *tw_exec(char *opts)
125 {
126         char *opts2;
127
128         if (check_version_enabled && !task_check_version()) {
129                 log_err("ptask is not compatible with the installed version of "
130                         "taskwarrior. The command line option -f can force "
131                         "the usage of an unsupported version of taskwarrior "
132                         "(risk of malfunction like damaging data).");
133                 return NULL;
134         }
135
136         opts2 = malloc(strlen("rc.confirmation:no ")
137                        + strlen(opts)
138                        + 1);
139         strcpy(opts2, "rc.confirmation:no ");
140         strcat(opts2, opts);
141
142         return task_exec(opts2);
143 }
144
145 static struct json_object *task_exec_json(const char *opts)
146 {
147         struct json_object *o;
148         char *str, *cmd;
149
150         cmd = malloc(strlen("rc.json.array=on ") + strlen(opts) + 1);
151         strcpy(cmd, "rc.json.array=on ");
152         strcat(cmd, opts);
153
154         str = tw_exec(cmd);
155
156         if (str) {
157                 o = json_tokener_parse(str);
158                 free(str);
159         } else {
160                 o = NULL;
161         }
162
163         free(cmd);
164
165         if (o && is_error(o))
166                 return NULL;
167
168         return o;
169 }
170
171 char **json_to_tags(struct json_object *jtask)
172 {
173         struct json_object *jtags, *jtag;
174         char **tags;
175         int n, i;
176
177         jtags = json_object_object_get(jtask, "tags");
178
179         if (!jtags)
180                 return NULL;
181
182
183         n = json_object_array_length(jtags);
184
185         tags = malloc((n + 1) * sizeof(char *));
186
187         for (i = 0; i < n; i++) {
188                 jtag = json_object_array_get_idx(jtags, i);
189                 tags[i] = strdup(json_object_get_string(jtag));
190         }
191
192         tags[n] = NULL;
193
194         return tags;
195 }
196
197 struct task *json_to_task(struct json_object *jtask)
198 {
199         struct task *task;
200         const char *urg;
201         struct json_object *json;
202
203         task = malloc(sizeof(struct task));
204
205         json = json_object_object_get(jtask, "id");
206         task->id = json_object_get_int(json);
207
208         json = json_object_object_get(jtask, "description");
209         task->description = strdup(json_object_get_string(json));
210
211         json = json_object_object_get(jtask, "status");
212         task->status = strdup(json_object_get_string(json));
213
214         json = json_object_object_get(jtask, "project");
215         if (json)
216                 task->project
217                         = strdup(json_object_get_string(json));
218         else
219                 task->project = strdup("");
220
221         json = json_object_object_get(jtask, "priority");
222         if (json)
223                 task->priority
224                         = strdup(json_object_get_string(json));
225         else
226                 task->priority = strdup("");
227
228         json = json_object_object_get(jtask, "uuid");
229         task->uuid = strdup(json_object_get_string(json));
230
231         json = json_object_object_get(jtask, "urgency");
232         urg = json_object_get_string(json);
233         if (urg)
234                 task->urgency = strdup(urg);
235         else
236                 task->urgency = NULL;
237
238         task->note = note_get(task->uuid);
239
240         json = json_object_object_get(jtask, "entry");
241         task->entry = parse_time(json_object_get_string(json));
242
243         json = json_object_object_get(jtask, "due");
244         if (json)
245                 task->due
246                         = parse_time(json_object_get_string(json));
247         else
248                 task->due = NULL;
249
250         json = json_object_object_get(jtask, "start");
251         if (json)
252                 task->start
253                         = parse_time(json_object_get_string(json));
254         else
255                 task->start = NULL;
256
257         json = json_object_object_get(jtask, "recur");
258         if (json)
259                 task->recur = strdup(json_object_get_string(json));
260         else
261                 task->recur = NULL;
262
263         task->tags = json_to_tags(jtask);
264
265         return task;
266 }
267
268 struct task **tw_get_all_tasks(const char *status)
269 {
270         int i, n;
271         struct json_object *jtasks, *jtask;
272         struct task **tasks;
273         char *opts;
274
275         opts = malloc(strlen("export status:") + strlen(status) + 1);
276
277         strcpy(opts, "export status:");
278         strcat(opts, status);
279
280         jtasks = task_exec_json(opts);
281         free(opts);
282
283         if (!jtasks)
284                 return NULL;
285
286         n = json_object_array_length(jtasks);
287
288         tasks = malloc((n + 1) * sizeof(struct task *));
289
290         for (i = 0; i < n; i++) {
291                 jtask = json_object_array_get_idx(jtasks, i);
292
293                 tasks[i] = json_to_task(jtask);
294         }
295
296         tasks[n] = NULL;
297
298         json_object_put(jtasks);
299
300         return tasks;
301 }
302
303 static char *escape(const char *txt)
304 {
305         char *result;
306         char *c;
307
308         result = malloc(2*strlen(txt)+1);
309         c = result;
310
311         while (*txt) {
312                 switch (*txt) {
313                 case '"':
314                 case '$':
315                 case '&':
316                 case '<':
317                 case '>':
318                         *c = '\\'; c++;
319                         *c = *txt;
320                         break;
321                 default:
322                         *c = *txt;
323                 }
324                 c++;
325                 txt++;
326         }
327
328         *c = '\0';
329
330         return result;
331 }
332
333 void tw_modify_description(const char *uuid, const char *newdesc)
334 {
335         char *opts;
336
337         opts = malloc(1
338                       + strlen(uuid)
339                       + strlen(" modify :\"")
340                       + strlen(newdesc)
341                       + strlen("\"")
342                       + 1);
343         sprintf(opts, " %s modify \"%s\"", uuid, newdesc);
344
345         tw_exec(opts);
346
347         free(opts);
348 }
349
350 void tw_modify_project(const char *uuid, const char *newproject)
351 {
352         char *str;
353         char *opts;
354
355         str = escape(newproject);
356
357         opts = malloc(1
358                       + strlen(uuid)
359                       + strlen(" modify project:\"")
360                       + strlen(str)
361                       + strlen("\"")
362                       + 1);
363         sprintf(opts, " %s modify project:\"%s\"", uuid, str);
364
365         tw_exec(opts);
366
367         free(str);
368         free(opts);
369 }
370
371 void tw_modify_priority(const char *uuid, const char *priority)
372 {
373         char *str;
374         char *opts;
375
376         log_fct_enter();
377
378         str = escape(priority);
379
380         opts = malloc(1
381                       + strlen(uuid)
382                       + strlen(" modify priority:\"")
383                       + strlen(str)
384                       + strlen("\"")
385                       + 1);
386         sprintf(opts, " %s modify priority:\"%s\"", uuid, str);
387
388         tw_exec(opts);
389
390         free(str);
391         free(opts);
392
393         log_fct_exit();
394 }
395
396 void tw_add(const char *newdesc, const char *prj, const char *prio)
397 {
398         char *opts, *eprj;
399
400         log_fct_enter();
401
402         eprj = escape(prj);
403
404         opts = malloc(strlen("add")
405                       + strlen(" priority:")
406                       + 1
407                       + strlen(" project:\\\"")
408                       + strlen(eprj)
409                       + strlen("\\\"")
410                       + strlen(" \"")
411                       + strlen(newdesc)
412                       + strlen("\"")
413                       + 1);
414
415         strcpy(opts, "add");
416
417         if (prio && strlen(prio) == 1) {
418                 strcat(opts, " priority:");
419                 strcat(opts, prio);
420         }
421
422         if (eprj && strlen(prj)) {
423                 strcat(opts, " project:\\\"");
424                 strcat(opts, eprj);
425                 strcat(opts, "\\\"");
426         }
427
428         strcat(opts, " \"");
429         strcat(opts, newdesc);
430         strcat(opts, "\"");
431
432         tw_exec(opts);
433
434         free(opts);
435         free(eprj);
436
437         log_fct_exit();
438 }
439
440 void tw_task_done(const char *uuid)
441 {
442         char *opts;
443
444         opts = malloc(1
445                       + strlen(uuid)
446                       + strlen(" done")
447                       + 1);
448         sprintf(opts, " %s done", uuid);
449
450         tw_exec(opts);
451
452         free(opts);
453 }
454
455 void tw_task_start(const char *uuid)
456 {
457         char *opts;
458
459         opts = malloc(1
460                       + strlen(uuid)
461                       + strlen(" start")
462                       + 1);
463         sprintf(opts, " %s start", uuid);
464
465         tw_exec(opts);
466
467         free(opts);
468 }
469
470 void tw_task_stop(const char *uuid)
471 {
472         char *opts;
473
474         opts = malloc(1
475                       + strlen(uuid)
476                       + strlen(" stop")
477                       + 1);
478         sprintf(opts, " %s stop", uuid);
479
480         tw_exec(opts);
481
482         free(opts);
483 }
484
485 void tw_task_remove(const char *uuid)
486 {
487         char *opts;
488
489         opts = malloc(1
490                       + strlen(uuid)
491                       + strlen(" delete")
492                       + 1);
493         sprintf(opts, " %s delete", uuid);
494
495         tw_exec(opts);
496
497         free(opts);
498 }
499
500 static void task_free(struct task *task)
501 {
502         char **tags;
503
504         if (!task)
505                 return ;
506
507         free(task->description);
508         free(task->status);
509         free(task->uuid);
510         free(task->note);
511         free(task->project);
512         free(task->priority);
513         free(task->urgency);
514         free(task->entry);
515         free(task->due);
516         free(task->start);
517         free(task->recur);
518
519         tags = task->tags;
520         if (tags) {
521                 while (*tags) {
522                         free(*tags);
523                         tags++;
524                 }
525                 free(task->tags);
526         }
527
528         free(task);
529 }
530
531 void tw_task_list_free(struct task **tasks)
532 {
533         struct task **cur;
534
535         if (!tasks)
536                 return ;
537
538         for (cur = tasks; *cur; cur++)
539                 task_free(*cur);
540
541         free(tasks);
542 }
543
544 static void project_free(struct project *p)
545 {
546         if (!p)
547                 return ;
548
549         free(p->name);
550         free(p);
551 }
552
553 void tw_project_list_free(struct project **prjs)
554 {
555         struct project **cur;
556
557         if (!prjs)
558                 return ;
559
560         for (cur = prjs; *cur; cur++)
561                 project_free(*cur);
562
563         free(prjs);
564 }
565
566 static struct project *project_list_get(struct project **prjs, const char *name)
567 {
568         struct project **cur;
569
570         for (cur = prjs; *cur; cur++)
571                 if (!strcmp((*cur)->name, name))
572                         return *cur;
573
574         return NULL;
575 }
576
577 static struct project *project_new(const char *name, int count)
578 {
579         struct project *prj;
580
581         prj = malloc(sizeof(struct project));
582
583         prj->name = strdup(name);
584         prj->count = count;
585
586         return prj;
587 }
588
589 static int projects_length(struct project **list)
590 {
591         int n;
592
593         if (!list)
594                 return 0;
595
596         n = 0;
597         while (*list) {
598                 n++;
599                 list++;
600         }
601
602         return n;
603 }
604
605 static struct project **projects_add(struct project **list, void *item)
606 {
607         int n;
608         struct project **result;
609
610         n = projects_length(list);
611
612         result = (struct project **)malloc
613                 ((n + 1 + 1) * sizeof(struct project *));
614
615         if (list)
616                 memcpy(result, list, n * sizeof(struct project *));
617
618         result[n] = item;
619         result[n + 1] = NULL;
620
621         return result;
622 }
623
624 struct project **tw_get_projects(struct task **tasks)
625 {
626         struct task **t_cur;
627         struct project **prjs, **tmp, *prj;
628         const char *prj_name;
629
630         log_fct_enter();
631
632         prjs = malloc(2 * sizeof(struct project *));
633         prjs[0] = project_new("ALL", 0);
634         prjs[1] = NULL;
635
636         for (t_cur = tasks; *t_cur; t_cur++) {
637                 prj_name = (*t_cur)->project;
638                 prj = project_list_get(prjs, prj_name);
639                 if (prj) {
640                         prj->count++;
641                 } else {
642                         prj = project_new(prj_name, 1);
643
644                         tmp = projects_add(prjs, prj);
645
646                         free(prjs);
647                         prjs = tmp;
648                 }
649                 prjs[0]->count++;
650         }
651
652         log_fct_exit();
653
654         return prjs;
655 }
656
657 void tw_enable_check_version(int e)
658 {
659         check_version_enabled = e;
660 }