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