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