43760d4202e43dd7f361d0375ccbab78a2ee2c0d
[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 void tw_task_remove(const char *uuid)
363 {
364         char *opts;
365
366         opts = malloc(1
367                       + strlen(uuid)
368                       + strlen(" delete")
369                       + 1);
370         sprintf(opts, " %s delete", uuid);
371
372         tw_exec(opts);
373
374         free(opts);
375 }
376
377 static void task_free(struct task *task)
378 {
379         if (!task)
380                 return ;
381
382         free(task->description);
383         free(task->status);
384         free(task->uuid);
385         free(task->note);
386         free(task->project);
387         free(task->priority);
388
389         free(task);
390 }
391
392 void tw_task_list_free(struct task **tasks)
393 {
394         struct task **cur;
395
396         if (!tasks)
397                 return ;
398
399         for (cur = tasks; *cur; cur++)
400                 task_free(*cur);
401
402         free(tasks);
403 }
404
405 static void project_free(struct project *p)
406 {
407         if (!p)
408                 return ;
409
410         free(p->name);
411         free(p);
412 }
413
414 void tw_project_list_free(struct project **prjs)
415 {
416         struct project **cur;
417
418         if (!prjs)
419                 return ;
420
421         for (cur = prjs; *cur; cur++)
422                 project_free(*cur);
423
424         free(prjs);
425 }
426
427 static struct project *project_list_get(struct project **prjs, const char *name)
428 {
429         struct project **cur;
430
431         for (cur = prjs; *cur; cur++)
432                 if (!strcmp((*cur)->name, name))
433                         return *cur;
434
435         return NULL;
436 }
437
438 static struct project *project_new(const char *name, int count)
439 {
440         struct project *prj;
441
442         prj = malloc(sizeof(struct project));
443
444         prj->name = strdup(name);
445         prj->count = count;
446
447         return prj;
448 }
449
450 struct project **tw_get_projects(struct task **tasks)
451 {
452         struct task **t_cur;
453         struct project **prjs, **tmp, *prj;
454         const char *prj_name;
455
456         log_fct_enter();
457
458         prjs = malloc(2 * sizeof(struct project *));
459         prjs[0] = project_new("ALL", 0);
460         prjs[1] = NULL;
461
462         for (t_cur = tasks; *t_cur; t_cur++) {
463                 prj_name = (*t_cur)->project;
464                 prj = project_list_get(prjs, prj_name);
465                 if (prj) {
466                         prj->count++;
467                 } else {
468                         prj = project_new(prj_name, 1);
469
470                         tmp = (struct project **)list_add((void **)prjs, prj);
471
472                         free(prjs);
473                         prjs = tmp;
474                 }
475                 prjs[0]->count++;
476         }
477
478         log_fct_exit();
479
480         return prjs;
481 }