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