2b8269322d9be393e4155bff625d1a43f2963d12
[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 <log.h>
28 #include "note.h"
29 #include <pstr.h>
30 #include "tw.h"
31
32
33 static int has_taskrc()
34 {
35         char *home, *path;
36         int ret;
37         struct stat st;
38
39         home = getenv("HOME");
40
41         if (!home) {
42                 log_err("HOME environment variable not defined");
43                 return 0;
44         }
45
46         path = malloc(strlen(home) + 1 + strlen(".taskrc") + 1);
47         sprintf(path, "%s/%s", home, ".taskrc");
48
49         ret = lstat(path, &st);
50
51         free(path);
52
53         return ret == 0;
54 }
55
56 static char *task_exec(char *opts)
57 {
58         FILE *f;
59         int ret;
60         size_t s;
61         char *str, *tmp, *cmd, buf[1024];
62
63         cmd = malloc(strlen("task ") + strlen(opts) + 1);
64         strcpy(cmd, "task ");
65         strcat(cmd, opts);
66
67         log_debug("execute: %s", cmd);
68
69         f = popen(cmd, "r");
70
71         free(cmd);
72
73         if (!f) {
74                 perror("popen");
75                 return NULL;
76         }
77
78         str = strdup("");
79         while ((s = fread(buf, 1, 1024, f))) {
80                 tmp = malloc(strlen(str) + s + (size_t)1);
81                 memcpy(tmp, str, strlen(str));
82                 memcpy(tmp + strlen(str), buf, s);
83                 tmp[strlen(str) + s] = '\0';
84                 free(str);
85                 str = tmp;
86         }
87
88         ret = pclose(f);
89
90         if (ret == -1)
91                 log_err("pclose fails");
92
93         return str;
94 }
95
96 static char *task_get_version()
97 {
98         char *out;
99
100         out = task_exec("--version");
101
102         trim(out);
103
104         return out;
105 }
106
107 static int task_check_version()
108 {
109         char *ver;
110
111         ver = task_get_version();
112
113         if (!ver)
114                 return 0;
115
116         log_debug("task version: %s", ver);
117
118         if (!strcmp(ver, "2.2.0"))
119                 return 1;
120         else
121                 return 0;
122 }
123
124 static char *tw_exec(char *opts)
125 {
126         if (!has_taskrc())
127                 return NULL;
128
129         if (!task_check_version()) {
130                 log_err("ptask is not compatible with the installed version of"
131                         " taskwarrior.");
132                 return NULL;
133         }
134
135         return task_exec(opts);
136 }
137
138 static struct json_object *task_exec_json(char *opts)
139 {
140         struct json_object *o;
141         char *str, *cmd;
142
143         cmd = malloc(strlen("rc.json.array=on ") + strlen(opts) + 1);
144         strcpy(cmd, "rc.json.array=on ");
145         strcat(cmd, opts);
146
147         str = tw_exec(cmd);
148
149         if (str) {
150                 o = json_tokener_parse(str);
151                 free(str);
152         } else {
153                 o = NULL;
154         }
155
156         free(cmd);
157
158         return o;
159 }
160
161 struct task **tw_get_all_tasks(const char *status)
162 {
163         int i, n;
164         struct json_object *jtasks, *jtask, *json;
165         struct task **tasks;
166         char *opts;
167
168         opts = malloc(strlen("export status:") + strlen(status) + 1);
169         sprintf(opts, "export status:%s", status);
170
171         jtasks = task_exec_json(opts);
172         free(opts);
173
174         if (!jtasks)
175                 return NULL;
176
177         n = json_object_array_length(jtasks);
178
179         tasks = malloc((n + 1) * sizeof(struct task *));
180
181         for (i = 0; i < n; i++) {
182                 jtask = json_object_array_get_idx(jtasks, i);
183
184                 tasks[i] = malloc(sizeof(struct task));
185
186                 json = json_object_object_get(jtask, "id");
187                 tasks[i]->id = json_object_get_int(json);
188
189                 json = json_object_object_get(jtask, "description");
190                 tasks[i]->description = strdup(json_object_get_string(json));
191
192                 json = json_object_object_get(jtask, "status");
193                 tasks[i]->status = strdup(json_object_get_string(json));
194
195                 json = json_object_object_get(jtask, "project");
196                 if (json)
197                         tasks[i]->project
198                                 = strdup(json_object_get_string(json));
199                 else
200                         tasks[i]->project = NULL;
201
202                 json = json_object_object_get(jtask, "priority");
203                 if (json)
204                         tasks[i]->priority
205                                 = strdup(json_object_get_string(json));
206                 else
207                         tasks[i]->priority = strdup("");
208
209                 json = json_object_object_get(jtask, "uuid");
210                 tasks[i]->uuid = strdup(json_object_get_string(json));
211
212                 tasks[i]->note = note_get(tasks[i]->uuid);
213         }
214
215         tasks[n] = NULL;
216
217         json_object_put(jtasks);
218
219         return tasks;
220 }
221
222 static char *escape(const char *txt)
223 {
224         char *result;
225         char *c;
226
227         result = malloc(2*strlen(txt)+1);
228         c = result;
229
230         while (*txt) {
231                 switch (*txt) {
232                 case '"':
233                         *c = '\\'; c++;
234                         *c = '"';
235                         break;
236                 case '$':
237                         *c = '\\'; c++;
238                         *c = '$';
239                         break;
240                 case '&':
241                         *c = '\\'; c++;
242                         *c = '&';
243                         break;
244                 default:
245                         *c = *txt;
246                 }
247                 c++;
248                 txt++;
249         }
250
251         *c = '\0';
252
253         return result;
254 }
255
256 void tw_modify_description(const char *uuid, const char *newdesc)
257 {
258         char *str;
259         char *opts;
260
261         str = escape(newdesc);
262
263         opts = malloc(1
264                       + strlen(uuid)
265                       + strlen(" modify :\"")
266                       + strlen(str)
267                       + strlen("\"")
268                       + 1);
269         sprintf(opts, " %s modify \"%s\"", uuid, str);
270
271         tw_exec(opts);
272
273         free(str);
274         free(opts);
275 }
276
277 void tw_modify_project(const char *uuid, const char *newproject)
278 {
279         char *str;
280         char *opts;
281
282         str = escape(newproject);
283
284         opts = malloc(1
285                       + strlen(uuid)
286                       + strlen(" modify project:\"")
287                       + strlen(str)
288                       + strlen("\"")
289                       + 1);
290         sprintf(opts, " %s modify project:\"%s\"", uuid, str);
291
292         tw_exec(opts);
293
294         free(str);
295         free(opts);
296 }
297
298 void tw_modify_priority(const char *uuid, const char *priority)
299 {
300         char *str;
301         char *opts;
302
303         str = escape(priority);
304
305         opts = malloc(1
306                       + strlen(uuid)
307                       + strlen(" modify priority:\"")
308                       + strlen(str)
309                       + strlen("\"")
310                       + 1);
311         sprintf(opts, " %s modify priority:\"%s\"", uuid, str);
312
313         tw_exec(opts);
314
315         free(str);
316         free(opts);
317 }
318
319 void tw_add(const char *newdesc)
320 {
321         char *str;
322         char *opts;
323
324         str = escape(newdesc);
325
326         opts = malloc(1
327                       + strlen(" add \"")
328                       + strlen(str)
329                       + strlen("\"")
330                       + 1);
331         sprintf(opts, " add \"%s\"", str);
332
333         tw_exec(opts);
334
335         free(str);
336         free(opts);
337 }
338
339 void tw_done(const char *uuid)
340 {
341         char *opts;
342
343         opts = malloc(1
344                       + strlen(uuid)
345                       + strlen(" done")
346                       + 1);
347         sprintf(opts, " %s done", uuid);
348
349         tw_exec(opts);
350
351         free(opts);
352 }
353
354 static void task_free(struct task *task)
355 {
356         if (!task)
357                 return ;
358
359         free(task->description);
360         free(task->status);
361         free(task->uuid);
362         free(task->note);
363         free(task->project);
364         free(task->priority);
365
366         free(task);
367 }
368
369 void tw_task_list_free(struct task **tasks)
370 {
371         struct task **cur;
372
373         if (!tasks)
374                 return ;
375
376         for (cur = tasks; *cur; cur++)
377                 task_free(*cur);
378
379         free(tasks);
380 }