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