]> Joshua Wise's Git repositories - tdl.git/blob - add.c
a9b2df7537d3ba6d7a350dbedb0a5488ed1dfa9a
[tdl.git] / add.c
1 /*
2    $Header: /cvs/src/tdl/add.c,v 1.16.2.1 2004/01/07 00:09:05 richard Exp $
3   
4    tdl - A console program for managing to-do lists
5    Copyright (C) 2001,2002,2003,2004,2005  Richard P. Curnow
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20    */
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include "tdl.h"
25
26 static int add_new_node(char *parent_path, int set_done, int set_priority, enum Priority new_priority, time_t insert_time, char *child_text)/*{{{*/
27 {
28   struct node *parent = NULL;
29   struct node *nn;
30
31   if (parent_path) {
32     parent = lookup_node(parent_path, 0, NULL);
33     if (!parent) return -1;
34   } else {
35     struct node *narrow_top;
36     narrow_top = get_narrow_top();
37     if (narrow_top) {
38       parent = narrow_top;
39     } else {
40       /* If all else fails, i.e. new node is a top-level one. */
41       parent = NULL;
42     }
43   }
44   
45   nn = new_node();
46   nn->text = new_string(child_text);
47   nn->arrived = (long) insert_time;
48   if (set_done) {
49     nn->done = (long) insert_time;
50   }
51   
52   nn->priority = (parent && !set_priority) ? parent->priority
53                                            : new_priority;
54
55   prepend_child(nn, parent);
56
57   /* Clear done status of parents - they can't be any longer! */
58   if (!set_done) {
59     while (parent) {
60       parent->done = 0;
61       parent = parent->parent;
62     }
63   }
64
65   return 0;
66 }
67 /*}}}*/
68 static char *make_composite(char *prefix, char *suffix)/*{{{*/
69 {
70   int plen, slen;
71   int tlen;
72   char *result;
73   int suffix_is_dot;
74   
75   plen = prefix ? strlen(prefix) : 0;
76   slen = suffix ? strlen(suffix) : 0;
77   suffix_is_dot = slen ? (!strcmp(suffix,".")) : 0;
78   tlen = plen + slen;
79   if (plen && slen) ++tlen; /* for internal '.' */
80   if (!plen && !slen) return NULL;
81   result = new_array(char, 1+tlen);
82   result[0] = '\0';
83   if (plen) {
84     strcat(result, prefix);
85     if (slen && !suffix_is_dot) strcat(result, ".");
86   }
87   if (slen && !suffix_is_dot) strcat(result, suffix);
88   return result;
89 }
90 /*}}}*/
91 static int try_add_interactive(char *parent_path, int set_done)/*{{{*/
92 {
93   char *text;
94   time_t insert_time;
95   int error = 0;
96   int blank;
97   int status;
98   char prompt[128];
99   char *prompt_base;
100   char *composite_index;
101
102   composite_index = make_composite(get_narrow_prefix(), parent_path);
103
104   prompt_base = set_done ? "log" : "add";
105   if (composite_index) {
106     sprintf(prompt, "%s (%s)> ", prompt_base, composite_index);
107   } else {
108     sprintf(prompt, "%s> ", prompt_base);
109   }
110   if (composite_index) free(composite_index);
111   
112   do {
113     text = interactive_text(prompt, NULL, &blank, &error);
114     if (error < 0) return error;
115     if (!blank) {
116       time(&insert_time);
117       status = add_new_node(parent_path, set_done, 0, PRI_NORMAL, insert_time, text);
118       free(text);
119       if (status < 0) return status;
120     }
121   } while (!blank);
122   return 0;
123 }
124 /*}}}*/
125 static int could_be_index(char *x)/*{{{*/
126 {
127   enum {AFTER_DIGIT, AFTER_PERIOD, AFTER_MINUS} state;
128   char *p;
129   if (x[0] == '.') return 1; /* To handle alphabetical matching */
130   state = AFTER_PERIOD;
131   for (p=x; *p; p++) {
132     switch (state) {
133       case AFTER_DIGIT:
134         if (isdigit(*p))    state = AFTER_DIGIT;
135         else if (*p == '-') state = AFTER_MINUS;
136         else if (*p == '.') state = AFTER_PERIOD;
137         else return 0;
138         break;
139       case AFTER_PERIOD:
140         if (isdigit(*p))    state = AFTER_DIGIT;
141         else if (*p == '-') state = AFTER_MINUS;
142         else return 0;
143         break;
144       case AFTER_MINUS:
145         if (isdigit(*p))    state = AFTER_DIGIT;
146         else return 0;
147         break;
148     }
149   }
150   return 1;
151 }
152 /*}}}*/
153 static int process_add_internal(char **x, int set_done)/*{{{*/
154 {
155   /* Expect 1 argument, the string to add.  Need other options at some point. */
156   time_t insert_time;
157   int argc = count_args(x);
158   char *text = NULL;
159   char *parent_path = NULL;
160   enum Priority priority = PRI_NORMAL;
161   int set_priority = 0;
162   char *x0;
163   int error;
164
165   insert_time = time(NULL);
166
167   if ((argc > 1) && (x[0][0] == '@')) {
168     int error;
169     /* For 'add', want date to be in the future.
170      * For 'log', want date to be in the past, as for 'done' */
171     int default_positive = set_done ? 0 : 1;
172     insert_time = parse_date(x[0]+1, insert_time, default_positive, &error);
173     if (error < 0) return error;
174     argc--;
175     x++;
176   }
177   
178   switch (argc) {
179     case 0:
180       return try_add_interactive(NULL, set_done);
181       break;
182     case 1:
183       if (could_be_index(x[0])) {
184         return try_add_interactive(x[0], set_done);
185       } else {
186         text = x[0];
187       }
188       break;
189     case 2:
190       text = x[1];
191       x0 = x[0];
192       if (isdigit(x0[0]) || (x0[0] == '.') || (x0[0] == '-') || (x0[0] == '+')) {
193         parent_path = x[0];
194       } else {
195         priority = parse_priority(x0, &error);
196         if (error < 0) return error;
197         set_priority = 1;
198       }
199       break;
200     case 3:
201       text = x[2];
202       priority = parse_priority(x[1], &error);
203       if (error < 0) return error;
204       set_priority = 1;
205       parent_path = x[0];
206       break;
207
208     default:
209       fprintf(stderr, "Usage : add [@<datespec>] [<parent_index>] [<priority>] <entry_text>\n");
210       return -1;
211       break;
212   }
213
214   return add_new_node(parent_path, set_done, set_priority, priority, insert_time, text);
215
216   return 0;
217 }
218 /*}}}*/
219 int process_add(char **x)/*{{{*/
220 {
221   return process_add_internal(x, 0);
222 }
223 /*}}}*/
224 int process_log(char **x)/*{{{*/
225 {
226   return process_add_internal(x, 1);
227 }
228 /*}}}*/
229 static void modify_tree_arrival_time(struct node *y, time_t new_time)/*{{{*/
230 {
231   struct node *c;
232   for (c = y->kids.next; c != (struct node *) &y->kids; c = c->chain.next) {
233     c->arrived = new_time;
234     if (has_kids(c)) {
235       modify_tree_arrival_time(c, new_time);
236     }
237   }
238 }
239 /*}}}*/
240 static int internal_postpone_open(char **x, time_t when)/*{{{*/
241 {
242   struct node *n;
243   int do_descendents;
244   
245   while (*x) {
246     do_descendents = include_descendents(*x); /* May modify *x */
247     n = lookup_node(*x, 0, NULL);
248     if (n) {
249       n->arrived = when;
250       if (do_descendents) modify_tree_arrival_time(n, when);
251     }
252     x++;
253   }
254   return 0;
255 }
256 /*}}}*/
257 int process_postpone(char **x)/*{{{*/
258 {
259   return internal_postpone_open(x, POSTPONED_TIME);
260 }
261 /*}}}*/
262 int process_open(char **x)/*{{{*/
263 {
264   return internal_postpone_open(x, time(NULL));
265 }
266 /*}}}*/
267 int process_defer(char **x)/*{{{*/
268 {
269   int argc;
270   time_t new_start_time;
271   int error;
272   char *date_start;
273
274   argc = count_args(x);
275   if (argc < 2) {
276     fprintf(stderr, "Usage: defer <datespec> <index>...\n");
277     return -1;
278   }
279
280   date_start = x[0];
281   new_start_time = time(NULL);
282   /* 'defer' always takes a date so the @ is optional. */
283   if (*date_start == '@') date_start++;
284   new_start_time = parse_date(date_start, new_start_time, 1, &error);
285   return internal_postpone_open(x+1, new_start_time);
286 }
287 /*}}}*/
288 int process_edit(char **x) /*{{{*/
289 {
290   int argc;
291   struct node *n;
292
293   argc = count_args(x);
294
295   if ((argc < 1) || (argc > 2)) {
296     fprintf(stderr, "Usage: edit <path> [<new_text>]\n");
297     return -1;
298   }
299
300   n = lookup_node(x[0], 0, NULL);
301   if (!n) return -1;
302   if (argc == 2) {
303     free(n->text);
304     n->text = new_string(x[1]);
305   } else {
306     int error;
307     int is_blank;
308     char *new_text;
309     char prompt[128];
310     char *composite_index;
311
312     composite_index = make_composite(get_narrow_prefix(), x[0]);
313     assert(composite_index);
314     sprintf(prompt, "edit (%s)> ", composite_index);
315     new_text = interactive_text(prompt, n->text, &is_blank, &error);
316     free(composite_index);
317     if (error < 0) return error;
318     if (!is_blank) {
319       free(n->text);
320       n->text = new_text; /* We own the pointer now */
321     }
322   }
323
324   return 0;
325 }
326 /*}}}*/
This page took 0.030212 seconds and 2 git commands to generate.