Imported Debian patch 0.0.5-1ubuntu1
[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 <list.h>
30 #include <log.h>
31 #include "note.h"
32 #include <pstr.h>
33 #include "tw.h"
34
35 struct tm *parse_time(const char *t)
36 {
37         struct tm *tm;
38
39         tm = malloc(sizeof(struct tm));
40         memset(tm, 0, sizeof(struct tm));
41         strptime(t, "%Y%m%dT%H%M%S%Z", tm);
42
43         return tm;
44 }
45
46 static char *task_exec(char *opts)
47 {
48         FILE *f;
49         int ret;
50         size_t s;
51         char *str, *tmp, *cmd, buf[1024];
52
53         log_fct_enter();
54
55         cmd = malloc(strlen("task ") + strlen(opts) + 1);
56         strcpy(cmd, "task ");
57         strcat(cmd, opts);
58
59         log_debug("execute: %s", cmd);
60
61         f = popen(cmd, "r");
62
63         free(cmd);
64
65         if (!f) {
66                 perror("popen");
67                 return NULL;
68         }
69
70         str = strdup("");
71         while ((s = fread(buf, 1, 1024, f))) {
72                 tmp = malloc(strlen(str) + s + (size_t)1);
73                 memcpy(tmp, str, strlen(str));
74                 memcpy(tmp + strlen(str), buf, s);
75                 tmp[strlen(str) + s] = '\0';
76                 free(str);
77                 str = tmp;
78         }
79
80         ret = pclose(f);
81
82         if (ret == -1)
83                 log_err("pclose fails");
84
85         log_fct_exit();
86
87         return str;
88 }
89
90 static char *task_get_version()
91 {
92         char *out;
93
94         out = task_exec("--version");
95
96         trim(out);
97
98         return out;
99 }
100
101 static int task_check_version()
102 {
103         char *ver;
104
105         ver = task_get_version();
106
107         if (!ver)
108                 return 0;
109
110         log_debug("task version: %s", ver);
111
112         if (!strcmp(ver, "2.2.0") || !strcmp(ver, "2.0.0"))
113                 return 1;
114         else
115                 return 0;
116 }
117
118 static char *tw_exec(char *opts)
119 {
120         char *opts2;
121
122         if (!task_check_version()) {
123                 log_err("ptask is not compatible with the installed version of"
124                         " taskwarrior.");
125                 return NULL;
126         }
127
128         opts2 = malloc(strlen("rc.confirmation:no ")
129                        + strlen(opts)
130                        + 1);
131         strcpy(opts2, "rc.confirmation:no ");
132         strcat(opts2, opts);
133
134         return task_exec(opts2);
135 }
136
137 static struct json_object *task_exec_json(const char *opts)
138 {
139         struct json_object *o;
140         char *str, *cmd;
141
142         cmd = malloc(strlen("rc.json.array=on ") + strlen(opts) + 1);
143         strcpy(cmd, "rc.json.array=on ");
144         strcat(cmd, opts);
145
146         str = tw_exec(cmd);
147
148         if (str) {
149                 o = json_tokener_parse(str);
150                 free(str);
151         } else {
152                 o = NULL;
153         }
154
155         free(cmd);
156
157         return o;
158 }
159
160 struct task **tw_get_all_tasks(const char *status)
161 {
162         int i, n;
163         struct json_object *jtasks, *jtask, *json;
164         struct task **tasks;
165         char *opts;
166         const char *urg;
167
168         opts = malloc(strlen("export status:") + strlen(status) + 1);
169
170         strcpy(opts, "export status:");
171         strcat(opts, status);
172
173         jtasks = task_exec_json(opts);
174         free(opts);
175
176         if (!jtasks)
177                 return NULL;
178
179         n = json_object_array_length(jtasks);
180
181         tasks = malloc((n + 1) * sizeof(struct task *));
182
183         for (i = 0; i < n; i++) {
184                 jtask = json_object_array_get_idx(jtasks, i);
185
186                 tasks[i] = malloc(sizeof(struct task));
187
188                 json = json_object_object_get(jtask, "id");
189                 tasks[i]->id = json_object_get_int(json);
190
191                 json = json_object_object_get(jtask, "description");
192                 tasks[i]->description = strdup(json_object_get_string(json));
193
194                 json = json_object_object_get(jtask, "status");
195                 tasks[i]->status = strdup(json_object_get_string(json));
196
197                 json = json_object_object_get(jtask, "project");
198                 if (json)
199                         tasks[i]->project
200                                 = strdup(json_object_get_string(json));
201                 else
202                         tasks[i]->project = strdup("");
203
204                 json = json_object_object_get(jtask, "priority");
205                 if (json)
206                         tasks[i]->priority
207                                 = strdup(json_object_get_string(json));
208                 else
209                         tasks[i]->priority = strdup("");
210
211                 json = json_object_object_get(jtask, "uuid");
212                 tasks[i]->uuid = strdup(json_object_get_string(json));
213
214                 json = json_object_object_get(jtask, "urgency");
215                 urg = json_object_get_string(json);
216                 if (urg)
217                         tasks[i]->urgency = strdup(urg);
218                 else
219                         tasks[i]->urgency = NULL;
220
221                 tasks[i]->note = note_get(tasks[i]->uuid);
222
223                 json = json_object_object_get(jtask, "entry");
224                 tasks[i]->entry = parse_time(json_object_get_string(json));
225
226                 json = json_object_object_get(jtask, "due");
227                 if (json)
228                         tasks[i]->due
229                                 = parse_time(json_object_get_string(json));
230                 else
231                         tasks[i]->due = NULL;
232
233                 json = json_object_object_get(jtask, "start");
234                 if (json)
235                         tasks[i]->start
236                                 = parse_time(json_object_get_string(json));
237                 else
238                         tasks[i]->start = NULL;
239         }
240
241         tasks[n] = NULL;
242
243         json_object_put(jtasks);
244
245         return tasks;
246 }
247
248 static char *escape(const char *txt)
249 {
250         char *result;
251         char *c;
252
253         result = malloc(2*strlen(txt)+1);
254         c = result;
255
256         while (*txt) {
257                 switch (*txt) {
258                 case '"':
259                 case '$':
260                 case '&':
261                 case '<':
262                 case '>':
263                         *c = '\\'; c++;
264                         *c = *txt;
265                         break;
266                 default:
267                         *c = *txt;
268                 }
269                 c++;
270                 txt++;
271         }
272
273         *c = '\0';
274
275         return result;
276 }
277
278 void tw_modify_description(const char *uuid, const char *newdesc)
279 {
280         char *opts;
281
282         opts = malloc(1
283                       + strlen(uuid)
284                       + strlen(" modify :\"")
285                       + strlen(newdesc)
286                       + strlen("\"")
287                       + 1);
288         sprintf(opts, " %s modify \"%s\"", uuid, newdesc);
289
290         tw_exec(opts);
291
292         free(opts);
293 }
294
295 void tw_modify_project(const char *uuid, const char *newproject)
296 {
297         char *str;
298         char *opts;
299
300         str = escape(newproject);
301
302         opts = malloc(1
303                       + strlen(uuid)
304                       + strlen(" modify project:\"")
305                       + strlen(str)
306                       + strlen("\"")
307                       + 1);
308         sprintf(opts, " %s modify project:\"%s\"", uuid, str);
309
310         tw_exec(opts);
311
312         free(str);
313         free(opts);
314 }
315
316 void tw_modify_priority(const char *uuid, const char *priority)
317 {
318         char *str;
319         char *opts;
320
321         log_fct_enter();
322
323         str = escape(priority);
324
325         opts = malloc(1
326                       + strlen(uuid)
327                       + strlen(" modify priority:\"")
328                       + strlen(str)
329                       + strlen("\"")
330                       + 1);
331         sprintf(opts, " %s modify priority:\"%s\"", uuid, str);
332
333         tw_exec(opts);
334
335         free(str);
336         free(opts);
337
338         log_fct_exit();
339 }
340
341 void tw_add(const char *newdesc, const char *prj, const char *prio)
342 {
343         char *opts, *eprj;
344
345         log_fct_enter();
346
347         eprj = escape(prj);
348
349         opts = malloc(strlen("add")
350                       + strlen(" priority:")
351                       + 1
352                       + strlen(" project:\\\"")
353                       + strlen(eprj)
354                       + strlen("\\\"")
355                       + strlen(" \"")
356                       + strlen(newdesc)
357                       + strlen("\"")
358                       + 1);
359
360         strcpy(opts, "add");
361
362         if (prio && strlen(prio) == 1) {
363                 strcat(opts, " priority:");
364                 strcat(opts, prio);
365         }
366
367         if (eprj && strlen(prj)) {
368                 strcat(opts, " project:\\\"");
369                 strcat(opts, eprj);
370                 strcat(opts, "\\\"");
371         }
372
373         strcat(opts, " \"");
374         strcat(opts, newdesc);
375         strcat(opts, "\"");
376
377         tw_exec(opts);
378
379         free(opts);
380         free(eprj);
381
382         log_fct_exit();
383 }
384
385 void tw_done(const char *uuid)
386 {
387         char *opts;
388
389         opts = malloc(1
390                       + strlen(uuid)
391                       + strlen(" done")
392                       + 1);
393         sprintf(opts, " %s done", uuid);
394
395         tw_exec(opts);
396
397         free(opts);
398 }
399
400 void tw_task_remove(const char *uuid)
401 {
402         char *opts;
403
404         opts = malloc(1
405                       + strlen(uuid)
406                       + strlen(" delete")
407                       + 1);
408         sprintf(opts, " %s delete", uuid);
409
410         tw_exec(opts);
411
412         free(opts);
413 }
414
415 static void task_free(struct task *task)
416 {
417         if (!task)
418                 return ;
419
420         free(task->description);
421         free(task->status);
422         free(task->uuid);
423         free(task->note);
424         free(task->project);
425         free(task->priority);
426         free(task->urgency);
427         free(task->entry);
428         free(task->due);
429         free(task->start);
430
431         free(task);
432 }
433
434 void tw_task_list_free(struct task **tasks)
435 {
436         struct task **cur;
437
438         if (!tasks)
439                 return ;
440
441         for (cur = tasks; *cur; cur++)
442                 task_free(*cur);
443
444         free(tasks);
445 }
446
447 static void project_free(struct project *p)
448 {
449         if (!p)
450                 return ;
451
452         free(p->name);
453         free(p);
454 }
455
456 void tw_project_list_free(struct project **prjs)
457 {
458         struct project **cur;
459
460         if (!prjs)
461                 return ;
462
463         for (cur = prjs; *cur; cur++)
464                 project_free(*cur);
465
466         free(prjs);
467 }
468
469 static struct project *project_list_get(struct project **prjs, const char *name)
470 {
471         struct project **cur;
472
473         for (cur = prjs; *cur; cur++)
474                 if (!strcmp((*cur)->name, name))
475                         return *cur;
476
477         return NULL;
478 }
479
480 static struct project *project_new(const char *name, int count)
481 {
482         struct project *prj;
483
484         prj = malloc(sizeof(struct project));
485
486         prj->name = strdup(name);
487         prj->count = count;
488
489         return prj;
490 }
491
492 struct project **tw_get_projects(struct task **tasks)
493 {
494         struct task **t_cur;
495         struct project **prjs, **tmp, *prj;
496         const char *prj_name;
497
498         log_fct_enter();
499
500         prjs = malloc(2 * sizeof(struct project *));
501         prjs[0] = project_new("ALL", 0);
502         prjs[1] = NULL;
503
504         for (t_cur = tasks; *t_cur; t_cur++) {
505                 prj_name = (*t_cur)->project;
506                 prj = project_list_get(prjs, prj_name);
507                 if (prj) {
508                         prj->count++;
509                 } else {
510                         prj = project_new(prj_name, 1);
511
512                         tmp = (struct project **)list_add((void **)prjs, prj);
513
514                         free(prjs);
515                         prjs = tmp;
516                 }
517                 prjs[0]->count++;
518         }
519
520         log_fct_exit();
521
522         return prjs;
523 }