]> Joshua Wise's Git repositories - tdl.git/blob - add.c
Fall back on index for sort if the due date is the same, not just if both due dates...
[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, time_t required_by, 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   nn->required_by = required_by;
52   
53   nn->priority = (parent && !set_priority) ? parent->priority
54                                            : new_priority;
55
56   prepend_child(nn, parent);
57
58   /* Clear done status of parents - they can't be any longer! */
59   if (!set_done) {
60     while (parent) {
61       parent->done = 0;
62       parent = parent->parent;
63     }
64   }
65
66   return 0;
67 }
68 /*}}}*/
69 static char *make_composite(char *prefix, char *suffix)/*{{{*/
70 {
71   int plen, slen;
72   int tlen;
73   char *result;
74   int suffix_is_dot;
75   
76   plen = prefix ? strlen(prefix) : 0;
77   slen = suffix ? strlen(suffix) : 0;
78   suffix_is_dot = slen ? (!strcmp(suffix,".")) : 0;
79   tlen = plen + slen;
80   if (plen && slen) ++tlen; /* for internal '.' */
81   if (!plen && !slen) return NULL;
82   result = new_array(char, 1+tlen);
83   result[0] = '\0';
84   if (plen) {
85     strcat(result, prefix);
86     if (slen && !suffix_is_dot) strcat(result, ".");
87   }
88   if (slen && !suffix_is_dot) strcat(result, suffix);
89   return result;
90 }
91 /*}}}*/
92 static int try_add_interactive(char *parent_path, int set_done)/*{{{*/
93 {
94   char *text;
95   time_t insert_time;
96   int error = 0;
97   int blank;
98   int status;
99   char prompt[128];
100   char *prompt_base;
101   char *composite_index;
102
103   composite_index = make_composite(get_narrow_prefix(), parent_path);
104
105   prompt_base = set_done ? "log" : "add";
106   if (composite_index) {
107     sprintf(prompt, "%s (%s)> ", prompt_base, composite_index);
108   } else {
109     sprintf(prompt, "%s> ", prompt_base);
110   }
111   if (composite_index) free(composite_index);
112   
113   do {
114     text = interactive_text(prompt, NULL, &blank, &error);
115     if (error < 0) return error;
116     if (!blank) {
117       time(&insert_time);
118       status = add_new_node(parent_path, set_done, 0, PRI_NORMAL, insert_time, 0, text);
119       free(text);
120       if (status < 0) return status;
121     }
122   } while (!blank);
123   return 0;
124 }
125 /*}}}*/
126 static int could_be_index(char *x)/*{{{*/
127 {
128   enum {AFTER_DIGIT, AFTER_PERIOD, AFTER_MINUS} state;
129   char *p;
130   if (x[0] == '.') return 1; /* To handle alphabetical matching */
131   state = AFTER_PERIOD;
132   for (p=x; *p; p++) {
133     switch (state) {
134       case AFTER_DIGIT:
135         if (isdigit(*p))    state = AFTER_DIGIT;
136         else if (*p == '-') state = AFTER_MINUS;
137         else if (*p == '.') state = AFTER_PERIOD;
138         else return 0;
139         break;
140       case AFTER_PERIOD:
141         if (isdigit(*p))    state = AFTER_DIGIT;
142         else if (*p == '-') state = AFTER_MINUS;
143         else return 0;
144         break;
145       case AFTER_MINUS:
146         if (isdigit(*p))    state = AFTER_DIGIT;
147         else return 0;
148         break;
149     }
150   }
151   return 1;
152 }
153 /*}}}*/
154 static int process_add_internal(char **x, int set_done)/*{{{*/
155 {
156   /* Expect 1 argument, the string to add.  Need other options at some point. */
157   time_t insert_time;
158   int argc = count_args(x);
159   char *text = NULL;
160   char *parent_path = NULL;
161   enum Priority priority = PRI_NORMAL;
162   int set_priority = 0;
163   char *x0;
164   int error;
165   time_t required_by;
166
167   insert_time = time(NULL);
168   required_by = 0;
169
170   if ((argc > 1) && (x[0][0] == '@')) {
171     int error;
172     /* For 'add', want date to be in the future.
173      * For 'log', want date to be in the past, as for 'done' */
174     int default_positive = set_done ? 0 : 1;
175     insert_time = parse_date(x[0]+1, insert_time, default_positive, &error);
176     if (error < 0) return error;
177     argc--;
178     x++;
179   }
180   
181   if ((argc > 1) && (x[0][0] == '+')) {
182     int error;
183     required_by = parse_date(x[0]+1, insert_time, 1, &error);
184     if (error < 0) return error;
185     argc--;
186     x++;
187   }
188   
189   switch (argc) {
190     case 0:
191       return try_add_interactive(NULL, set_done);
192       break;
193     case 1:
194       if (could_be_index(x[0])) {
195         return try_add_interactive(x[0], set_done);
196       } else {
197         text = x[0];
198       }
199       break;
200     case 2:
201       text = x[1];
202       x0 = x[0];
203       if (isdigit(x0[0]) || (x0[0] == '.') || (x0[0] == '-') || (x0[0] == '+')) {
204         parent_path = x[0];
205       } else {
206         priority = parse_priority(x0, &error);
207         if (error < 0) return error;
208         set_priority = 1;
209       }
210       break;
211     case 3:
212       text = x[2];
213       priority = parse_priority(x[1], &error);
214       if (error < 0) return error;
215       set_priority = 1;
216       parent_path = x[0];
217       break;
218
219     default:
220       fprintf(stderr, "Usage : add [@<starting datespec>] [+<due datespec>] [<parent_index>] [<priority>] <entry_text>\n");
221       return -1;
222       break;
223   }
224
225   return add_new_node(parent_path, set_done, set_priority, priority, insert_time, required_by, text);
226
227   return 0;
228 }
229 /*}}}*/
230 int process_add(char **x)/*{{{*/
231 {
232   return process_add_internal(x, 0);
233 }
234 /*}}}*/
235 int process_log(char **x)/*{{{*/
236 {
237   return process_add_internal(x, 1);
238 }
239 /*}}}*/
240 static void modify_tree_arrival_time(struct node *y, time_t new_time)/*{{{*/
241 {
242   struct node *c;
243   for (c = y->kids.next; c != (struct node *) &y->kids; c = c->chain.next) {
244     c->arrived = new_time;
245     if (has_kids(c)) {
246       modify_tree_arrival_time(c, new_time);
247     }
248   }
249 }
250 /*}}}*/
251 static int internal_postpone_open(char **x, time_t when)/*{{{*/
252 {
253   struct node *n;
254   int do_descendents;
255   
256   while (*x) {
257     do_descendents = include_descendents(*x); /* May modify *x */
258     n = lookup_node(*x, 0, NULL);
259     if (n) {
260       n->arrived = when;
261       if (do_descendents) modify_tree_arrival_time(n, when);
262     }
263     x++;
264   }
265   return 0;
266 }
267 /*}}}*/
268 int process_postpone(char **x)/*{{{*/
269 {
270   return internal_postpone_open(x, POSTPONED_TIME);
271 }
272 /*}}}*/
273 int process_open(char **x)/*{{{*/
274 {
275   return internal_postpone_open(x, time(NULL));
276 }
277 /*}}}*/
278 int process_defer(char **x)/*{{{*/
279 {
280   int argc;
281   time_t new_start_time;
282   int error;
283   char *date_start;
284
285   argc = count_args(x);
286   if (argc < 2) {
287     fprintf(stderr, "Usage: defer <datespec> <index>...\n");
288     return -1;
289   }
290
291   date_start = x[0];
292   new_start_time = time(NULL);
293   /* 'defer' always takes a date so the @ is optional. */
294   if (*date_start == '@') date_start++;
295   new_start_time = parse_date(date_start, new_start_time, 1, &error);
296   return internal_postpone_open(x+1, new_start_time);
297 }
298 /*}}}*/
299 int process_edit(char **x) /*{{{*/
300 {
301   int argc;
302   struct node *n;
303
304   argc = count_args(x);
305
306   if ((argc < 1) || (argc > 2)) {
307     fprintf(stderr, "Usage: edit <path> [<new_text>]\n");
308     return -1;
309   }
310
311   n = lookup_node(x[0], 0, NULL);
312   if (!n) return -1;
313   if (argc == 2) {
314     free(n->text);
315     n->text = new_string(x[1]);
316   } else {
317     int error;
318     int is_blank;
319     char *new_text;
320     char prompt[128];
321     char *composite_index;
322
323     composite_index = make_composite(get_narrow_prefix(), x[0]);
324     assert(composite_index);
325     sprintf(prompt, "edit (%s)> ", composite_index);
326     new_text = interactive_text(prompt, n->text, &is_blank, &error);
327     free(composite_index);
328     if (error < 0) return error;
329     if (!is_blank) {
330       free(n->text);
331       n->text = new_text; /* We own the pointer now */
332     }
333   }
334
335   return 0;
336 }
337 /*}}}*/
This page took 0.036983 seconds and 4 git commands to generate.