]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | $Header: /cvs/src/tdl/util.c,v 1.8.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 <ctype.h> | |
23 | #include "tdl.h" | |
24 | ||
25 | int count_args(char **x)/*{{{*/ | |
26 | { | |
27 | int n = 0; | |
28 | while (*x) { | |
29 | n++; | |
30 | x++; | |
31 | } | |
32 | return n; | |
33 | } | |
34 | /*}}}*/ | |
35 | int include_descendents(char *x)/*{{{*/ | |
36 | { | |
37 | /* Check if string ends in ... . If it does, truncate that off. */ | |
38 | int len; | |
39 | int result = 0; | |
40 | len = strlen(x); | |
41 | if (len >= 4) { | |
42 | if (!strcmp(x + (len-3), "...")) { | |
43 | result = 1; | |
44 | x[len-3] = 0; | |
45 | } | |
46 | } | |
47 | return result; | |
48 | } | |
49 | /*}}}*/ | |
50 | static char *ordinal(int n)/*{{{*/ | |
51 | { | |
52 | int nn; | |
53 | nn = n % 10; | |
54 | switch (nn) { | |
55 | case 1: return "st"; | |
56 | case 2: return "nd"; | |
57 | case 3: return "rd"; | |
58 | default: return "th"; | |
59 | } | |
60 | } | |
61 | /*}}}*/ | |
62 | struct node *lookup_node(char *path, int allow_zero_index, struct node **parent)/*{{{*/ | |
63 | { | |
64 | char *p = path; | |
65 | int n, nc, idx, tidx, aidx, ncomp; | |
66 | int direction; | |
67 | struct links *x; | |
68 | struct node *y = NULL; | |
69 | struct node *narrow_top; | |
70 | ||
71 | narrow_top = get_narrow_top(); | |
72 | if (narrow_top) { | |
73 | /* Special case to allow user to do operations on the node to which the | |
74 | * view is currently narrowed. (This doesn't apply to 'top' which is just a | |
75 | * skeleton entry.) */ | |
76 | if (!strcmp(path, ".")) { | |
77 | return narrow_top; | |
78 | } | |
79 | x = &(narrow_top->kids); | |
80 | } else { | |
81 | x = ⊤ | |
82 | } | |
83 | ||
84 | ncomp = 1; | |
85 | if (parent) *parent = NULL; | |
86 | ||
87 | /* Skip leading '.', if any. */ | |
88 | if (*p == '.') p++; | |
89 | ||
90 | while (*p) { | |
91 | if ((p[0] == '-') || isdigit(p[0])) { | |
92 | n = sscanf(p, "%d%n", &idx, &nc); | |
93 | if (n != 1) { | |
94 | fprintf(stderr, "Bad path expression found, starting [%s]\n", p); | |
95 | return NULL; | |
96 | } | |
97 | p += nc; | |
98 | ||
99 | if (idx > 0) { | |
100 | direction = 1; | |
101 | aidx = idx; | |
102 | } else if (idx < 0) { | |
103 | direction = 0; | |
104 | aidx = -idx; | |
105 | } else { | |
106 | if (allow_zero_index) { | |
107 | if (*p) { | |
108 | fprintf(stderr, "Zero index only allowed as last component\n"); | |
109 | return NULL; | |
110 | } else { | |
111 | /* This is a special cheat to allow inserting entries at | |
112 | the start or end of a chain for the 'above' and | |
113 | 'below' commands */ | |
114 | return (struct node *) x; | |
115 | } | |
116 | } else { | |
117 | fprintf(stderr, "Zero in index not allowed\n"); | |
118 | return NULL; | |
119 | } | |
120 | } | |
121 | ||
122 | if (x->next == (struct node *) x) { | |
123 | fprintf(stderr, "Path [%s] doesn't exist - tree not that deep\n", path); | |
124 | return NULL; | |
125 | } | |
126 | ||
127 | for (y = direction ? x->next : x->prev, tidx = aidx; --tidx;) { | |
128 | ||
129 | y = direction ? y->chain.next : y->chain.prev; | |
130 | ||
131 | if (y == (struct node *) x) { | |
132 | fprintf(stderr, "Can't find entry %d for %d%s component of path %s\n", | |
133 | idx, ncomp, ordinal(ncomp), path); | |
134 | return NULL; | |
135 | } | |
136 | } | |
137 | } else { | |
138 | /* Lookup by start of node text. */ | |
139 | char *dot; | |
140 | int len; | |
141 | struct node *match; | |
142 | dot = strchr(p, '.'); | |
143 | if (!dot) { /* final component. */ | |
144 | len = strlen(p); | |
145 | } else { | |
146 | len = dot - p; | |
147 | } | |
148 | match = NULL; | |
149 | for (y = x->next; y != (struct node *) x; y = y->chain.next) { | |
150 | if (!strncasecmp(y->text, p, len)) { | |
151 | if (match) { | |
152 | fprintf(stderr, "Ambiguous match for %d%s component (", | |
153 | ncomp, ordinal(ncomp)); | |
154 | fwrite(p, 1, len, stderr); | |
155 | fprintf(stderr, ") of path %s\n", path); | |
156 | return NULL; | |
157 | } | |
158 | match = y; | |
159 | } | |
160 | } | |
161 | if (!match) { | |
162 | fprintf(stderr, "Can't find entry for %d%s component (", | |
163 | ncomp, ordinal(ncomp)); | |
164 | fwrite(p, 1, len, stderr); | |
165 | fprintf(stderr, ") of path %s\n", path); | |
166 | } | |
167 | ||
168 | y = match; | |
169 | p += len; | |
170 | } | |
171 | ||
172 | if (*p == '.') { | |
173 | p++; | |
174 | x = &y->kids; | |
175 | if (parent) *parent = y; | |
176 | } | |
177 | ||
178 | ncomp++; | |
179 | } | |
180 | ||
181 | return y; | |
182 | } | |
183 | /*}}}*/ | |
184 | enum Priority parse_priority(char *priority, int *error)/*{{{*/ | |
185 | { | |
186 | enum Priority result; | |
187 | int is_digit; | |
188 | ||
189 | if (!priority) { | |
190 | *error = -1; | |
191 | return PRI_UNKNOWN; | |
192 | } else { | |
193 | ||
194 | is_digit = isdigit(priority[0]); | |
195 | ||
196 | if (is_digit) { | |
197 | int value = atoi(priority); | |
198 | result = (value >= PRI_URGENT) ? PRI_URGENT : | |
199 | (value <= PRI_VERYLOW) ? PRI_VERYLOW : | |
200 | (enum Priority) value; | |
201 | } else { | |
202 | int len = strlen(priority); | |
203 | if (!strncmp(priority, "urgent", len)) { | |
204 | result = PRI_URGENT; | |
205 | } else if (!strncmp(priority, "high", len)) { | |
206 | result = PRI_HIGH; | |
207 | } else if (!strncmp(priority, "normal", len)) { | |
208 | result = PRI_NORMAL; | |
209 | } else if (!strncmp(priority, "low", len)) { | |
210 | result = PRI_LOW; | |
211 | } else if (!strncmp(priority, "verylow", len)) { | |
212 | result = PRI_VERYLOW; | |
213 | } else { | |
214 | fprintf(stderr, "Can't parse priority '%s'\n", priority); | |
215 | *error = -1; | |
216 | return PRI_UNKNOWN; /* bogus */ | |
217 | } | |
218 | } | |
219 | } | |
220 | *error = 0; | |
221 | return result; | |
222 | } | |
223 | /*}}}*/ | |
224 | void clear_flags(struct links *x)/*{{{*/ | |
225 | { | |
226 | struct node *y; | |
227 | for (y = x->next; y != (struct node *) x; y = y->chain.next) { | |
228 | y->flag = 0; | |
229 | if (has_kids(y)) { | |
230 | clear_flags(&y->kids); | |
231 | } | |
232 | } | |
233 | } | |
234 | /*}}}*/ | |
235 | void mark_all_descendents(struct node *n)/*{{{*/ | |
236 | { | |
237 | struct node *y; | |
238 | for (y = n->kids.next; y != (struct node *) &n->kids; y = y->chain.next) { | |
239 | y->flag = 1; | |
240 | if (has_kids(y)) { | |
241 | mark_all_descendents(y); | |
242 | } | |
243 | } | |
244 | } | |
245 | /*}}}*/ | |
246 | int has_kids(struct node *x)/*{{{*/ | |
247 | { | |
248 | return (x->kids.next != (struct node *) &x->kids); | |
249 | } | |
250 | /*}}}*/ | |
251 | struct node *new_node(void)/*{{{*/ | |
252 | { | |
253 | struct node *result = new (struct node); | |
254 | result->parent = NULL; | |
255 | result->text = NULL; | |
256 | result->priority = PRI_NORMAL; | |
257 | result->arrived = result->required_by = result->done = 0U; | |
258 | result->kids.next = result->kids.prev = (struct node *) &result->kids; | |
259 | result->chain.next = result->chain.prev = (struct node *) &result->chain; | |
260 | return result; | |
261 | } | |
262 | /*}}}*/ | |
263 | void free_node(struct node *x)/*{{{*/ | |
264 | { | |
265 | /* FIXME : To be written */ | |
266 | ||
267 | ||
268 | } | |
269 | /*}}}*/ | |
270 | void append_node(struct node *n, struct links *l)/*{{{*/ | |
271 | { | |
272 | n->chain.next = l->next; | |
273 | n->chain.prev = (struct node *) l; | |
274 | l->next->chain.prev = n; | |
275 | l->next = n; | |
276 | } | |
277 | /*}}}*/ | |
278 | void prepend_node(struct node *n, struct links *l)/*{{{*/ | |
279 | { | |
280 | n->chain.prev = l->prev; | |
281 | n->chain.next = (struct node *) l; | |
282 | l->prev->chain.next = n; | |
283 | l->prev = n; | |
284 | } | |
285 | /*}}}*/ | |
286 | void prepend_child(struct node *child, struct node *parent)/*{{{*/ | |
287 | { | |
288 | child->parent = parent; | |
289 | if (parent) { | |
290 | prepend_node(child, &parent->kids); | |
291 | } else { | |
292 | struct node *narrow_top; | |
293 | narrow_top = get_narrow_top(); | |
294 | prepend_node(child, narrow_top ? &narrow_top->kids : &top); | |
295 | } | |
296 | } | |
297 | /*}}}*/ |