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