2 $Header: /cvs/src/tdl/inter.c,v 1.13.2.1 2004/01/07 00:09:05 richard Exp $
4 tdl - A console program for managing to-do lists
5 Copyright (C) 2001-2004 Richard P. Curnow
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.
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.
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
22 /* This file deals with interactive mode */
35 #include <readline/readline.h>
36 #include <readline/history.h>
39 #if USE_RL_COMPLETION_MATCHES
40 #define COMPLETION_MATCHES rl_completion_matches
42 #define COMPLETION_MATCHES completion_matches
49 static char *generate_a_command_completion(const char *text, int state)/*{{{*/
51 static int list_index, len;
59 while (list_index < n_cmds) {
60 int inter_ok = cmds[list_index].interactive_ok;
61 name = cmds[list_index].name;
63 if (inter_ok && !strncmp(name, text, len)) {
64 return new_string(name);
68 return NULL; /* For no matches */
71 static char *generate_a_priority_completion(const char *text, int state)/*{{{*/
73 static char *priorities[] = {"urgent", "high", "normal", "low", "verylow", NULL};
74 static int list_index, len;
82 while ((name = priorities[list_index])) {
84 if (!strncmp(name, text, len)) {
85 return new_string(name);
89 return NULL; /* For no matches */
93 /* Structure to build a single linked list of nodes, working towards the
96 struct node_chain *next;
98 struct node *start; /* To see if we've got back to the start of the ring */
102 static struct node_chain *start_chain(struct links *x, struct node_chain *parent)/*{{{*/
104 struct node_chain *result;
105 result = new(struct node_chain);
106 result->start = (struct node *) x;
107 result->node = x->next;
109 result->next = parent;
113 static void cleanup_chain(struct node_chain *chain)/*{{{*/
115 /* TODO: Reclaim memory */
119 static char *format_index(struct node_chain *chain)/*{{{*/
123 struct node_chain *x;
124 char buf[1024], buf1[32];
126 for (n=0, x=chain; x; n++, x=x->next) {
127 indices[n] = x->index;
132 sprintf(buf1, "%d", indices[n]);
140 return new_string(buf);
143 static struct node *advance_chain(struct node_chain **pchain, char **index_string)/*{{{*/
145 struct node_chain *chain = *pchain;
146 struct node *result = NULL;
149 fprintf(stderr, "advance chain, top index=%d\n", chain->index);
153 if (chain->node == chain->start) {
154 struct node_chain *next = chain->next;
158 chain->node = chain->node->chain.next;
161 fprintf(stderr, "Returning to outer level, index=%d\n", chain->index);
166 fprintf(stderr, "Got to outermost level\n");
171 } else if (has_kids(chain->node)) {
173 fprintf(stderr, "Node has children, scanning children next\n");
175 result = chain->node;
176 *index_string = format_index(chain);
177 /* Set-up to visit kids next time */
178 chain = start_chain(&chain->node->kids, chain);
183 fprintf(stderr, "ordinary case\n");
185 result = chain->node;
186 *index_string = format_index(chain);
187 chain->node = chain->node->chain.next;
197 static char *generate_a_done_completion(const char *text, int state)/*{{{*/
199 static struct node_chain *chain = NULL;
201 struct node *narrow_top;
205 load_database_if_not_loaded();
208 /* Re-initialise the node chain */
209 if (chain) cleanup_chain(chain);
210 narrow_top = get_narrow_top();
211 chain = start_chain((narrow_top ? &narrow_top->kids : &top), NULL);
215 while ((node = advance_chain(&chain, &buf))) {
216 int undone = (!node->done);
217 if (undone && !strncmp(text, buf, len)) {
220 /* Avoid gross memory leak */
228 /* Can't pass extra args thru to completers :-( */
229 static int want_postponed_entries = 0;
231 static char *generate_a_postpone_completion(const char *text, int state)/*{{{*/
233 static struct node_chain *chain = NULL;
235 struct node *narrow_top;
239 load_database_if_not_loaded();
242 /* Re-initialise the node chain */
243 if (chain) cleanup_chain(chain);
244 narrow_top = get_narrow_top();
245 chain = start_chain((narrow_top ? &narrow_top->kids : &top), NULL);
249 while ((node = advance_chain(&chain, &buf))) {
250 int is_done = (node->done > 0);
251 int not_postponed = (node->arrived != POSTPONED_TIME);
252 if (!is_done && (not_postponed ^ want_postponed_entries) && !strncmp(text, buf, len)) {
255 /* Avoid gross memory leak */
263 char **complete_help(char *text, int index)/*{{{*/
266 matches = COMPLETION_MATCHES(text, generate_a_command_completion);
270 char **default_completer(char *text, int index)/*{{{*/
272 if (cmds[index].synopsis) {
273 fprintf(stderr, "\n%s %s\n", cmds[index].name, cmds[index].synopsis);
279 char **complete_list(char *text, int index)/*{{{*/
282 if (text[0] && isalpha(text[0])) {
283 /* Try to complete priority */
284 matches = COMPLETION_MATCHES(text, generate_a_priority_completion);
287 return default_completer(text, index);
291 char **complete_priority(char *text, int index)/*{{{*/
293 return complete_list(text, index);
296 char **complete_postpone(char *text, int index)/*{{{*/
299 want_postponed_entries = 0;
300 matches = COMPLETION_MATCHES(text, generate_a_postpone_completion);
304 char **complete_open(char *text, int index)/*{{{*/
307 want_postponed_entries = 1;
308 matches = COMPLETION_MATCHES(text, generate_a_postpone_completion);
312 char **complete_done(char *text, int index)/*{{{*/
315 matches = COMPLETION_MATCHES(text, generate_a_done_completion);
320 static char **tdl_completion(char *text, int start, int end)/*{{{*/
322 char **matches = NULL;
324 matches = COMPLETION_MATCHES(text, generate_a_command_completion);
328 for (i=0; i<n_cmds; i++) {
329 if (!strncmp(rl_line_buffer, cmds[i].name, cmds[i].matchlen)) {
330 if (cmds[i].completer) {
331 matches = (cmds[i].completer)(text, i);
333 matches = default_completer(text, i);
343 static char **null_tdl_completion(char *text, int start, int end)/*{{{*/
349 char **complete_help(char *text, int index)/*{{{*/
353 char **complete_list(char *text, int index)/*{{{*/
357 char **complete_priority(char *text, int index)/*{{{*/
361 char **complete_postpone(char *text, int index)/*{{{*/
366 char **complete_open(char *text, int index)/*{{{*/
371 char **complete_done(char *text, int index)/*{{{*/
378 static void add_null_arg(char ***av, int *max, int *n)/*{{{*/
382 *av = grow_array(char *, *max, *av);
389 static void add_arg(char ***av, int *max, int *n, char *p, int len)/*{{{*/
396 *av = grow_array(char *, *max, *av);
399 (*av)[*n] = new_array(char, len + 1);
400 /* Make copy of string, rejecting any escape characters (backslashes) */
401 for (u=p, v=(*av)[*n], nn=len; nn>0; ) {
428 static char **argv = NULL;
430 static void split_line_and_dispatch(char *line)/*{{{*/
437 /* Poke in argv[0] */
439 add_arg(&argv, &max, &n, av0, strlen(av0));
441 /* Now, split the line and add each argument */
444 while (*p && isspace(*p)) p++;
447 /* p points to start of argument. */
448 if (*p == '\'' || *p == '"') {
451 /* Scan for matching terminator */
453 while (*q && (*q != t)) {
454 if (*q == '\\') q++; /* Escape following character */
455 if (*q) q++; /* Bogus backslash at end of line */
458 /* Single word arg */
460 while (*q && !isspace(*q)) q++;
462 add_arg(&argv, &max, &n, p, q - p);
467 add_null_arg(&argv, &max, &n);
469 /* For now, print arg list for debugging */
472 for (i=0; argv[i]; i++) {
473 printf("arg %d : <%s>\n", i, argv[i]);
482 /* Now free arguments */
483 for (i=0; argv[i]; i++) {
490 static int is_line_blank(char *line)/*{{{*/
493 for (p=line; *p; p++) {
494 if (!isspace(*p)) return 0;
499 static char *make_prompt(void)/*{{{*/
501 char *narrow_prefix = get_narrow_prefix();
505 length = strlen(narrow_prefix) + 8;
506 result = new_array(char, length);
507 strcpy(result, "tdl[");
508 strcat(result, narrow_prefix);
509 strcat(result, "]> ");
511 result = new_string("tdl> ");
517 static void interactive_readline(void)/*{{{*/
523 char *prompt = make_prompt();
524 cmd = readline(prompt);
527 /* At end of file (e.g. input is a script, or user hits ^D, or stdin (file
528 #0) is closed by the signal handler) */
531 /* Check if line is blank */
532 had_char = !is_line_blank(cmd);
536 split_line_and_dispatch(cmd);
546 static char *readline_initval = NULL;
547 static int setup_initval_hook(void)/*{{{*/
549 if (readline_initval) {
550 rl_insert_text(readline_initval);
556 static char *interactive_text_readline(char *prompt, char *initval, int *is_blank, int *error)/*{{{*/
559 Function *old_rl_pre_input_hook = NULL;
562 old_rl_pre_input_hook = rl_pre_input_hook;
564 readline_initval = initval;
565 rl_pre_input_hook = setup_initval_hook;
567 line = readline(prompt);
568 rl_pre_input_hook = old_rl_pre_input_hook;
569 readline_initval = NULL;
570 *is_blank = line ? is_line_blank(line) : 1;
571 if (line && !*is_blank) add_history(line);
576 static char *get_line_stdio(char *prompt, int *at_eof)/*{{{*/
594 line = grow_array(char, max, line);
607 static void interactive_stdio(void)/*{{{*/
613 line = get_line_stdio("tdl> ", &at_eof);
615 split_line_and_dispatch(line);
621 static char *interactive_text_stdio(char *prompt, char *initval, int *is_blank, int *error)/*{{{*/
626 line = get_line_stdio(prompt, &at_eof);
627 *is_blank = line ? is_line_blank(line) : 0;
631 char *interactive_text (char *prompt, char *initval, int *is_blank, int *error)/*{{{*/
636 rl_attempted_completion_function = (CPPFunction *) null_tdl_completion;
637 result = interactive_text_readline(prompt, initval, is_blank, error);
638 rl_attempted_completion_function = (CPPFunction *) tdl_completion;
641 /* In case someone wants to drive tdl from a script, by redirecting stdin to it. */
642 return interactive_text_stdio(prompt, initval, is_blank, error);
645 return interactive_text_stdio(prompt, initval, is_blank, error);
650 void interactive(void)/*{{{*/
652 /* Main interactive function */
655 rl_completion_entry_function = NULL;
656 rl_attempted_completion_function = (CPPFunction *) tdl_completion;
657 interactive_readline();
659 /* In case someone wants to drive tdl from a script, by redirecting stdin to it. */
665 if (argv) free(argv);
671 /*{{{ Main routine [for testing]*/
673 int main (int argc, char **argv) {