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