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