]> Joshua Wise's Git repositories - tdl.git/blame - list.c
get 'make install' to work right on case-insensitive systems
[tdl.git] / list.c
CommitLineData
7024e37b
JW
1/*
2 $Header: /cvs/src/tdl/list.c,v 1.20.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 <time.h>
23#include <string.h>
24#include <assert.h>
25#include <ctype.h>
26#include <unistd.h>
27#include "tdl.h"
28
29struct list_options {
30 unsigned monochrome:1;
31 unsigned show_all:1;
32 unsigned show_postponed:1;
33 unsigned verbose:1;
34 unsigned set_depth:1;
35 int depth;
36};
37
38#define INDENT_TAB 3
39
40/*{{{ Colour definitions */
41#define RED "\e[31m\e[1m"
42#define GREEN "\e[32m"
43#define YELLOW "\e[33m\e[1m"
44#define BLUE "\e[34m"
45#define MAGENTA "\e[35m"
46#define CYAN "\e[36m"
47#define NORMAL "\e[0m"
48#define DIM "\e[37m\e[2m"
49#define DIMCYAN "\e[36m\e[2m"
50
51/* Table to map priority levels to colours */
52static char *colour_table[] = {
53 NORMAL, BLUE, CYAN, NORMAL, YELLOW, RED
54};
55
56static char *priority_text[] = {
57 "UNKNOWN!", "verylow", "low", "normal", "high", "urgent"
58};
59
60/*}}}*/
61void do_indent(int indent)/*{{{*/
62{
63 int i;
64 for (i=0; i<indent; i++) putchar(' ');
65}
66/*}}}*/
67void do_bullet_indent(int indent)/*{{{*/
68{
69 int i;
70 int n;
71 n = indent - 2;
72 for (i=0; i<indent; i++) putchar((i == n) ? '-' : ' ');
73}
74/*}}}*/
75static void print_timestamp(int timestamp, char *leader, int indent, int monochrome)/*{{{*/
76{
77 char buffer[32];
78 time_t now, timestamp2;
79 long diff, days_ago, days_ahead;
80
81 now = time(NULL);
82 diff = now - timestamp;
83 days_ago = (diff + ((diff > 0) ? 43200 : -43200)) / 86400;
84 timestamp2 = (time_t) timestamp;
85 strftime(buffer, sizeof(buffer), "%a %d %b %Y %H:%M",
86 localtime(&timestamp2));
87 do_indent(indent+2);
88 if (days_ago < 0) {
89 days_ahead = - days_ago;
90 if (monochrome) {
91 printf("%s: %s (%ld day%s ahead)\n", leader, buffer, days_ago, (days_ahead == 1) ? "" : "s");
92 } else {
93 printf("%s%s:%s %s %s(%ld day%s ahead)%s\n", GREEN, leader, NORMAL, buffer, MAGENTA, days_ahead, (days_ahead == 1) ? "" : "s", NORMAL);
94 }
95 } else {
96 if (monochrome) {
97 printf("%s: %s (%ld day%s ago)\n", leader, buffer, days_ago, (days_ago == 1) ? "" : "s");
98 } else {
99 printf("%s%s:%s %s (%ld day%s ago)\n", GREEN, leader, NORMAL, buffer, days_ago, (days_ago == 1) ? "" : "s");
100 }
101 }
102}
103/*}}}*/
104static void count_kids(struct links *x, int *n_kids, int *n_done_kids, int *n_open_kids)/*{{{*/
105{
106 int nk, ndk;
107 struct node *y;
108
109 nk = ndk = 0;
110 for (y = x->next;
111 y != (struct node *) x;
112 y = y->chain.next) {
113 if (y->done) ndk++;
114 nk++;
115 }
116
117 if (n_kids) *n_kids = nk;
118 if (n_done_kids) *n_done_kids = ndk;
119 if (n_open_kids) *n_open_kids = (nk - ndk);
120 return;
121}
122/*}}}*/
123static void print_details(struct node *y, int indent, int summarise_kids, const struct list_options *options, char *index_buffer, time_t now)/*{{{*/
124{
125 int is_done;
126 int is_ignored;
127 int is_postponed;
128 int is_deferred;
129 char *p;
130 int n_kids, n_open_kids;
131 int show_state;
132 char *narrow_prefix;
133 int index_buffer_len;
134
135 is_done = (y->done > 0);
136 is_ignored = (y->done == IGNORED_TIME);
137 is_postponed = (y->arrived == POSTPONED_TIME);
138 is_deferred = (y->arrived > now);
139 if (!options->show_all && is_done) return;
140
141 do_indent(indent);
142 count_kids(&y->kids, &n_kids, NULL, &n_open_kids);
143 show_state = options->show_all || options->show_postponed;
144 index_buffer_len = strlen(index_buffer);
145 narrow_prefix = get_narrow_prefix();
146
147 if (narrow_prefix) {
148 if (options->monochrome) printf("%s%s", narrow_prefix, index_buffer_len ? "." : "");
149 else printf("%s%s%s%s", BLUE, narrow_prefix, index_buffer_len ? "." : "", NORMAL);
150 }
151
152 if (options->monochrome) printf("%s", index_buffer);
153 else printf("%s%s%s", GREEN, index_buffer, NORMAL);
154
155 if (summarise_kids && (n_kids > 0)) {
156 if (options->monochrome) printf(" [%d/%d]", n_open_kids, n_kids);
157 else printf(" %s[%d/%d]%s", CYAN, n_open_kids, n_kids, NORMAL);
158 }
159
160 if (show_state && !options->verbose) {
161 if (is_ignored) {
162 if (options->monochrome) printf(" (IGNORED)");
163 else printf(" %s(IGNORED)%s", BLUE, NORMAL);
164 } else if (is_done) {
165 if (options->monochrome) printf(" (DONE)");
166 else printf(" %s(DONE)%s", CYAN, NORMAL);
167 } else if (is_postponed) {
168 if (options->monochrome) printf(" (POSTPONED)");
169 else printf(" %s(POSTPONED)%s", MAGENTA, NORMAL);
170 } else if (is_deferred) {
171 if (options->monochrome) printf(" (DEFERRED)");
172 else printf(" %s(DEFERRED)%s", MAGENTA, NORMAL);
173 }
174 printf(" : ");
175 } else {
176 printf(" ");
177 }
178
179 if (!options->monochrome) printf("%s", is_done ? CYAN : is_postponed ? MAGENTA : colour_table[y->priority]);
180
181#if 0
182
183 if (summarise_kids && (n_kids > 0)) {
184 if (options->monochrome) {
185 printf("%s [%d/%d] %s", index_buffer, n_open_kids, n_kids,
186 (show_state && !options->verbose && is_ignored) ? "(IGNORED) : " :
187 (show_state && !options->verbose && is_done) ? "(DONE) : " :
188 (show_state && !options->verbose && is_done) ? "(DONE) : " :
189 (show_state && !options->verbose && (y->arrived > now)) ? "(DEFERRED) : " : ": ");
190 } else {
191 printf("%s%s %s[%d/%d]%s %s%s", GREEN, index_buffer, CYAN, n_open_kids, n_kids, NORMAL,
192 (show_state && !options->verbose && is_ignored) ? BLUE "(IGNORED) " NORMAL :
193 (show_state && !options->verbose && is_done) ? CYAN "(DONE) " NORMAL :
194 (show_state && !options->verbose && is_postponed) ? MAGENTA "(POSTPONED) " :
195 (show_state && !options->verbose && (y->arrived > now)) ? MAGENTA "(DEFERRED) " : "",
196 is_done ? CYAN : is_postponed ? MAGENTA : colour_table[y->priority]);
197 }
198 } else {
199 if (options->monochrome) {
200 printf("%s %s", index_buffer,
201 (show_state && !options->verbose && is_ignored) ? "(IGNORED) : " :
202 (show_state && !options->verbose && is_done) ? "(DONE) : " :
203 (show_state && !options->verbose && is_postponed) ? "(POSTPONED) : " :
204 (show_state && !options->verbose && (y->arrived > now)) ? "(DEFERRED) : " : ": ");
205 } else {
206 printf("%s%s%s %s%s", GREEN, index_buffer, NORMAL,
207 (show_state && !options->verbose && is_ignored) ? BLUE "(IGNORED) " NORMAL :
208 (show_state && !options->verbose && is_done) ? CYAN "(DONE) " NORMAL :
209 (show_state && !options->verbose && is_postponed) ? MAGENTA "(POSTPONED) " :
210 (show_state && !options->verbose && (y->arrived > now)) ? MAGENTA "(DEFERRED) " : "",
211 is_done ? CYAN : is_postponed ? MAGENTA : colour_table[y->priority]);
212 }
213 }
214#endif
215 for (p = y->text; *p; p++) {
216 putchar(*p);
217 if (*p == '\n') {
218 do_indent(indent + 5);
219 }
220 }
221 if (!options->monochrome) printf("%s", NORMAL);
222 printf("\n");
223
224 if (options->verbose) {
225 print_timestamp(y->arrived, "Arrived", indent, options->monochrome);
226 do_indent(indent + 2);
227 if (options->monochrome) {
228 printf("Priority: %s\n", priority_text[y->priority]);
229 } else {
230 printf("%sPriority: %s%s%s\n",
231 GREEN, colour_table[y->priority], priority_text[y->priority], NORMAL);
232 }
233 if (y->required_by > 0) print_timestamp(y->required_by, "Required by", indent, options->monochrome);
234 if (y->done > 0) print_timestamp(y->done, "Completed", indent, options->monochrome);
235 printf("\n");
236 }
237
238}
239/*}}}*/
240static void list_chain(struct links *x, int indent, int depth, const struct list_options *options, char *index_buffer, enum Priority prio, time_t now, unsigned char *hits)/*{{{*/
241{
242 struct node *y;
243 int idx, is_done, is_deferred, is_postponed;
244 int show_node;
245 char component_buffer[8];
246 char new_index_buffer[64];
247
248 for (y = x->next, idx = 1;
249 y != (struct node *) x;
250 y = y->chain.next, ++idx) {
251
252 is_done = (y->done > 0);
253 is_postponed = (y->arrived == POSTPONED_TIME);
254 is_deferred = (y->arrived > now);
255 show_node = options->show_all
256 || (options->show_postponed && !is_done)
257 || (!is_deferred && !is_postponed);
258 if (!show_node) continue;
259
260 sprintf(component_buffer, "%d", idx);
261 strcpy(new_index_buffer, index_buffer);
262 if (strlen(new_index_buffer) > 0) {
263 strcat(new_index_buffer, ".");
264 }
265 strcat(new_index_buffer, component_buffer);
266
267 if (y->priority >= prio) {
268 int summarise_kids = (options->set_depth && (options->depth == depth));
269 if (hits[y->iscratch]) {
270 print_details(y, indent, summarise_kids, options, new_index_buffer, now);
271 }
272 }
273
274 /* Maybe list children regardless of priority assigned to parent. */
275 if (!options->set_depth || (depth < options->depth)) {
276 list_chain(&y->kids, indent + INDENT_TAB, depth + 1, options, new_index_buffer, prio, now, hits);
277 }
278
279 }
280 return;
281}
282/*}}}*/
283static void allocate_indices(struct links *x, int *idx)/*{{{*/
284{
285 struct node *y;
286 for (y = x->next;
287 y != (struct node *) x;
288 y = y->chain.next) {
289
290 y->iscratch = *idx;
291 ++*idx;
292 allocate_indices(&y->kids, idx);
293 }
294}
295/*}}}*/
296static void search_node(struct links *x, int max_errors, unsigned long *vecs, unsigned long hitvec, unsigned char *hits)/*{{{*/
297{
298 struct node *y;
299 char *p;
300 char *token;
301 int got_hit;
302 unsigned long r0, r1, r2, r3, nr0, nr1, nr2;
303
304 for (y = x->next;
305 y != (struct node *) x;
306 y = y->chain.next) {
307
308 token = y->text;
309
310 switch (max_errors) {
311 /* optimise common cases for few errors to allow optimizer to keep bitmaps
312 * in registers */
313 case 0:/*{{{*/
314 r0 = ~0;
315 got_hit = 0;
316 for(p=token; *p; p++) {
317 int idx = (unsigned int) *(unsigned char *)p;
318 r0 = (r0<<1) | vecs[idx];
319 if (~(r0 | hitvec)) {
320 got_hit = 1;
321 break;
322 }
323 }
324 break;
325 /*}}}*/
326 case 1:/*{{{*/
327 r0 = ~0;
328 r1 = r0<<1;
329 got_hit = 0;
330 for(p=token; *p; p++) {
331 int idx = (unsigned int) *(unsigned char *)p;
332 nr0 = (r0<<1) | vecs[idx];
333 r1 = ((r1<<1) | vecs[idx]) & ((r0 & nr0) << 1) & r0;
334 r0 = nr0;
335 if (~((r0 & r1) | hitvec)) {
336 got_hit = 1;
337 break;
338 }
339 }
340 break;
341 /*}}}*/
342 case 2:/*{{{*/
343 r0 = ~0;
344 r1 = r0<<1;
345 r2 = r1<<1;
346 got_hit = 0;
347 for(p=token; *p; p++) {
348 int idx = (unsigned int) *(unsigned char *)p;
349 nr0 = (r0<<1) | vecs[idx];
350 nr1 = ((r1<<1) | vecs[idx]) & ((r0 & nr0) << 1) & r0;
351 r2 = ((r2<<1) | vecs[idx]) & ((r1 & nr1) << 1) & r1;
352 r0 = nr0;
353 r1 = nr1;
354 if (~((r0 & r1& r2) | hitvec)) {
355 got_hit = 1;
356 break;
357 }
358 }
359 break;
360 /*}}}*/
361 case 3:/*{{{*/
362 r0 = ~0;
363 r1 = r0<<1;
364 r2 = r1<<1;
365 r3 = r2<<1;
366 got_hit = 0;
367 for(p=token; *p; p++) {
368 int idx = (unsigned int) *(unsigned char *)p;
369 nr0 = (r0<<1) | vecs[idx];
370 nr1 = ((r1<<1) | vecs[idx]) & ((r0 & nr0) << 1) & r0;
371 nr2 = ((r2<<1) | vecs[idx]) & ((r1 & nr1) << 1) & r1;
372 r3 = ((r3<<1) | vecs[idx]) & ((r2 & nr2) << 1) & r2;
373 r0 = nr0;
374 r1 = nr1;
375 r2 = nr2;
376 if (~((r0 & r1 & r2 & r3) | hitvec)) {
377 got_hit = 1;
378 break;
379 }
380 }
381 break;
382 /*}}}*/
383 default:
384 assert(0); /* not allowed */
385 break;
386 }
387 if (got_hit) {
388 hits[y->iscratch] = 1;
389 }
390 search_node(&y->kids, max_errors, vecs, hitvec, hits);
391 }
392}
393/*}}}*/
394static void merge_search_condition(unsigned char *hits, int n_nodes, char *cond)/*{{{*/
395{
396 /* See "Fast text searching with errors, Sun Wu and Udi Manber, TR 91-11,
397 University of Arizona. I have been informed that this algorithm is NOT
398 patented. This implementation of it is entirely the work of Richard P.
399 Curnow - I haven't looked at any related source (webglimpse, agrep etc) in
400 writing this.
401 */
402
403 int max_errors;
404 char *slash;
405 char *substring;
406 unsigned long a[256];
407 unsigned long hit;
408 int len, i;
409 char *p;
410 unsigned char *hit0;
411
412 slash = strchr(cond, '/');
413 if (!slash) {
414 max_errors = 0;
415 substring = cond;
416 } else {
417 substring = new_string(cond);
418 substring[slash-cond] = '\0';
419 max_errors = atoi(slash+1);
420 if (max_errors > 3) {
421 fprintf(stderr, "Can only match with up to 3 errors, ignoring patterh <%s>\n", cond);
422 goto get_out;
423 }
424 }
425
426 len = strlen(substring);
427 if (len < 1 || len > 31) {
428 fprintf(stderr, "Pattern must be between 1 and 31 characters\n");
429 goto get_out;
430 }
431
432 /* Set array 'a' to all -1 values */
433 memset(a, 0xff, 256 * sizeof(unsigned long));
434 for (p=substring, i=0; *p; p++, i++) {
435 unsigned char pc;
436 pc = *(unsigned char *) p;
437 a[(unsigned int) pc] &= ~(1UL << i);
438 /* Make search case insensitive */
439 if (isupper(pc)) {
440 a[tolower((unsigned int) pc)] &= ~(1UL << i);
441 }
442 if (islower(pc)) {
443 a[toupper((unsigned int) pc)] &= ~(1UL << i);
444 }
445 }
446 hit = ~(1UL << (len-1));
447
448 hit0 = new_array(unsigned char, n_nodes);
449 memset(hit0, 0, n_nodes);
450
451 /* Now scan each node against this match criterion */
452 search_node(&top, max_errors, a, hit, hit0);
453 for (i=0; i<n_nodes; i++) {
454 hits[i] &= hit0[i];
455 }
456 free(hit0);
457
458get_out:
459 if (substring != cond) {
460 free(substring);
461 }
462 return;
463}
464/*}}}*/
465int process_list(char **x)/*{{{*/
466{
467 struct list_options options;
468 int options_done = 0;
469 int any_paths = 0;
470 char index_buffer[256];
471 char *y;
472 enum Priority prio = PRI_NORMAL, prio_to_use, node_prio;
473 int prio_set = 0;
474 time_t now = time(NULL);
475
476 unsigned char *hits;
477 int node_index, n_nodes;
478
479 options.monochrome = 0;
480 options.show_all = 0;
481 options.show_postponed = 0;
482 options.verbose = 0;
483 options.set_depth = 0;
484
485 if ( (getenv("TDL_LIST_MONOCHROME") != NULL) ||
486 (isatty(STDOUT_FILENO) == 0) ) {
487 options.monochrome = 1;
488 }
489
490 /* Initialisation to support searching */
491 node_index = 0;
492 allocate_indices(&top, &node_index);
493 n_nodes = node_index;
494
495 hits = n_nodes ? new_array(unsigned char, n_nodes) : NULL;
496
497 /* all nodes match until proven otherwise */
498 memset(hits, 1, n_nodes);
499
500 while ((y = *x) != 0) {
501 /* An argument starting '1' or '+1' or '+-1' (or '-1' after '--') is
502 * treated as the path of the top node to show */
503 if (isdigit(y[0]) ||
504 (y[0] == '.') ||
505 (options_done && (y[0] == '-') && isdigit(y[1])) ||
506 ((y[0] == '+') &&
507 (isdigit(y[1]) ||
508 ((y[1] == '-' && isdigit(y[2])))))) {
509
510 struct node *n = lookup_node(y, 0, NULL);
511 int summarise_kids;
512
513 if (!n) return -1;
514
515 any_paths = 1;
516 index_buffer[0] = '\0';
517 strcat(index_buffer, y);
518 summarise_kids = (options.set_depth && (options.depth==0));
519 if (hits[n->iscratch]) {
520 print_details(n, 0, summarise_kids, &options, index_buffer, now);
521 }
522 if (!options.set_depth || (options.depth > 0)) {
523 node_prio = n->priority;
524
525 /* If the priority has been set on the cmd line, always use that.
526 * Otherwise, use the priority from the specified node, _except_ when
527 * that is higher than normal, in which case use normal. */
528 prio_to_use = (prio_set) ? prio : ((node_prio > prio) ? prio : node_prio);
529 list_chain(&n->kids, INDENT_TAB, 0, &options, index_buffer, prio_to_use, now, hits);
530 }
531 } else if ((y[0] == '-') && (y[1] == '-')) {
532 options_done = 1;
533 } else if (y[0] == '-') {
534 while (*++y) {
535 switch (*y) {
536 case 'v':
537 options.verbose = 1;
538 break;
539 case 'a':
540 options.show_all = 1;
541 break;
542 case 'm':
543 options.monochrome = 1;
544 break;
545 case 'p':
546 options.show_postponed = 1;
547 break;
548 case '1': case '2': case '3':
549 case '4': case '5': case '6':
550 case '7': case '8': case '9':
551 options.set_depth = 1;
552 options.depth = (*y) - '1';
553 break;
554 default:
555 fprintf(stderr, "Unrecognized option : -%c\n", *y);
556 break;
557 }
558 }
559 } else if (y[0] == '/') {
560 /* search expression */
561 merge_search_condition(hits, n_nodes, y+1);
562
563 } else {
564 int error;
565 prio = parse_priority(y, &error);
566 if (error < 0) return error;
567 prio_set = 1;
568 }
569
570 x++;
571 }
572
573 if (!any_paths) {
574 struct node *narrow_top = get_narrow_top();
575 if (narrow_top) {
576 index_buffer[0] = 0;
577 if (hits[narrow_top->iscratch]) {
578 int summarise_kids = (options.set_depth && (options.depth==0));
579 print_details(narrow_top, 0, summarise_kids, &options, index_buffer, now);
580 }
581 if (!options.set_depth || (options.depth > 0)) {
582 list_chain(&narrow_top->kids, 0, 1, &options, index_buffer, prio, now, hits);
583 }
584 } else {
585 index_buffer[0] = 0;
586 list_chain(&top, 0, 0, &options, index_buffer, prio, now, hits);
587 }
588 }
589
590 if (hits) free(hits);
591
592 return 0;
593}
594/*}}}*/
This page took 0.068242 seconds and 4 git commands to generate.