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