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