]>
Commit | Line | Data |
---|---|---|
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 | /*}}}*/ |