89b2761f817ea8dc408877f1d543df51e3b41462
[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                 tasks[i]->note = note_get(tasks[i]->uuid);
201         }
202
203         tasks[n] = NULL;
204
205         json_object_put(jtasks);
206
207         return tasks;
208 }
209
210 static char *escape(const char *txt)
211 {
212         char *result;
213         char *c;
214
215         result = malloc(2*strlen(txt)+1);
216         c = result;
217
218         while (*txt) {
219                 switch (*txt) {
220                 case '"':
221                 case '$':
222                 case '&':
223                 case '<':
224                 case '>':
225                         *c = '\\'; c++;
226                         *c = *txt;
227                         break;
228                 default:
229                         *c = *txt;
230                 }
231                 c++;
232                 txt++;
233         }
234
235         *c = '\0';
236
237         return result;
238 }
239
240 void tw_modify_description(const char *uuid, const char *newdesc)
241 {
242         char *opts;
243
244         opts = malloc(1
245                       + strlen(uuid)
246                       + strlen(" modify :\"")
247                       + strlen(newdesc)
248                       + strlen("\"")
249                       + 1);
250         sprintf(opts, " %s modify \"%s\"", uuid, newdesc);
251
252         tw_exec(opts);
253
254         free(opts);
255 }
256
257 void tw_modify_project(const char *uuid, const char *newproject)
258 {
259         char *str;
260         char *opts;
261
262         str = escape(newproject);
263
264         opts = malloc(1
265                       + strlen(uuid)
266                       + strlen(" modify project:\"")
267                       + strlen(str)
268                       + strlen("\"")
269                       + 1);
270         sprintf(opts, " %s modify project:\"%s\"", uuid, str);
271
272         tw_exec(opts);
273
274         free(str);
275         free(opts);
276 }
277
278 void tw_modify_priority(const char *uuid, const char *priority)
279 {
280         char *str;
281         char *opts;
282
283         log_fct_enter();
284
285         str = escape(priority);
286
287         opts = malloc(1
288                       + strlen(uuid)
289                       + strlen(" modify priority:\"")
290                       + strlen(str)
291                       + strlen("\"")
292                       + 1);
293         sprintf(opts, " %s modify priority:\"%s\"", uuid, str);
294
295         tw_exec(opts);
296
297         free(str);
298         free(opts);
299
300         log_fct_exit();
301 }
302
303 void tw_add(const char *newdesc, const char *prj, const char *prio)
304 {
305         char *opts, *eprj;
306
307         log_fct_enter();
308
309         eprj = escape(prj);
310
311         opts = malloc(strlen("add")
312                       + strlen(" priority:")
313                       + 1
314                       + strlen(" project:\\\"")
315                       + strlen(eprj)
316                       + strlen("\\\"")
317                       + strlen(" \"")
318                       + strlen(newdesc)
319                       + strlen("\"")
320                       + 1);
321
322         strcpy(opts, "add");
323
324         if (prio && strlen(prio) == 1) {
325                 strcat(opts, " priority:");
326                 strcat(opts, prio);
327         }
328
329         if (eprj && strlen(prj)) {
330                 strcat(opts, " project:\\\"");
331                 strcat(opts, eprj);
332                 strcat(opts, "\\\"");
333         }
334
335         strcat(opts, " \"");
336         strcat(opts, newdesc);
337         strcat(opts, "\"");
338
339         tw_exec(opts);
340
341         free(opts);
342         free(eprj);
343
344         log_fct_exit();
345 }
346
347 void tw_done(const char *uuid)
348 {
349         char *opts;
350
351         opts = malloc(1
352                       + strlen(uuid)
353                       + strlen(" done")
354                       + 1);
355         sprintf(opts, " %s done", uuid);
356
357         tw_exec(opts);
358
359         free(opts);
360 }
361
362 static void task_free(struct task *task)
363 {
364         if (!task)
365                 return ;
366
367         free(task->description);
368         free(task->status);
369         free(task->uuid);
370         free(task->note);
371         free(task->project);
372         free(task->priority);
373
374         free(task);
375 }
376
377 void tw_task_list_free(struct task **tasks)
378 {
379         struct task **cur;
380
381         if (!tasks)
382                 return ;
383
384         for (cur = tasks; *cur; cur++)
385                 task_free(*cur);
386
387         free(tasks);
388 }
389
390 static void project_free(struct project *p)
391 {
392         if (!p)
393                 return ;
394
395         free(p->name);
396         free(p);
397 }
398
399 void tw_project_list_free(struct project **prjs)
400 {
401         struct project **cur;
402
403         if (!prjs)
404                 return ;
405
406         for (cur = prjs; *cur; cur++)
407                 project_free(*cur);
408
409         free(prjs);
410 }
411
412 static struct project *project_list_get(struct project **prjs, const char *name)
413 {
414         struct project **cur;
415
416         for (cur = prjs; *cur; cur++)
417                 if (!strcmp((*cur)->name, name))
418                         return *cur;
419
420         return NULL;
421 }
422
423 static struct project *project_new(const char *name, int count)
424 {
425         struct project *prj;
426
427         prj = malloc(sizeof(struct project));
428
429         prj->name = strdup(name);
430         prj->count = count;
431
432         return prj;
433 }
434
435 struct project **tw_get_projects(struct task **tasks)
436 {
437         struct task **t_cur;
438         struct project **prjs, **tmp, *prj;
439         const char *prj_name;
440
441         log_fct_enter();
442
443         prjs = malloc(2 * sizeof(struct project *));
444         prjs[0] = project_new("ALL", 0);
445         prjs[1] = NULL;
446
447         for (t_cur = tasks; *t_cur; t_cur++) {
448                 prj_name = (*t_cur)->project;
449                 prj = project_list_get(prjs, prj_name);
450                 if (prj) {
451                         prj->count++;
452                 } else {
453                         prj = project_new(prj_name, 1);
454
455                         tmp = (struct project **)list_add((void **)prjs, prj);
456
457                         free(prjs);
458                         prjs = tmp;
459                 }
460                 prjs[0]->count++;
461         }
462
463         log_fct_exit();
464
465         return prjs;
466 }