f10fbb6dec0414866ecd35da3df8d0607e1eae9f
[runtime.git] / apps / irssi / src / fe-common / core / completion.c
1 /*
2  completion.c : irssi
3
4     Copyright (C) 2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "commands.h"
24 #include "misc.h"
25 #include "lib-config/iconfig.h"
26 #include "settings.h"
27
28 #include "completion.h"
29
30 #define wordreplace_find(word) \
31         iconfig_list_find("replaces", "text", word, "replace")
32
33 #define completion_find(completion) \
34         iconfig_list_find("completions", "short", completion, "long")
35
36 static GList *complist; /* list of commands we're currently completing */
37 static char *last_line;
38 static int last_want_space, last_line_pos;
39
40 #define isseparator_notspace(c) \
41         ((c) == ',')
42
43 #define isseparator(c) \
44         (i_isspace(c) || isseparator_notspace(c))
45
46 void chat_completion_init(void);
47 void chat_completion_deinit(void);
48
49 /* Return whole word at specified position in string */
50 char *get_word_at(const char *str, int pos, char **startpos)
51 {
52         const char *start, *end;
53
54         g_return_val_if_fail(str != NULL, NULL);
55         g_return_val_if_fail(pos >= 0, NULL);
56
57         /* get previous word if char at `pos' is space */
58         start = str+pos;
59         while (start > str && isseparator(start[-1])) start--;
60
61         end = start;
62         while (start > str && !isseparator(start[-1])) start--;
63         while (*end != '\0' && !isseparator(*end)) end++;
64         while (*end != '\0' && isseparator_notspace(*end)) end++;
65
66         *startpos = (char *) start;
67         return g_strndup(start, (int) (end-start));
68 }
69
70 /* automatic word completion - called when space/enter is pressed */
71 char *auto_word_complete(const char *line, int *pos)
72 {
73         GString *result;
74         const char *replace;
75         char *word, *wordstart, *ret;
76         int startpos;
77
78         g_return_val_if_fail(line != NULL, NULL);
79         g_return_val_if_fail(pos != NULL, NULL);
80
81         word = get_word_at(line, *pos, &wordstart);
82         startpos = (int) (wordstart-line);
83
84         result = g_string_new(line);
85         g_string_erase(result, startpos, strlen(word));
86
87         /* check for words in autocompletion list */
88         replace = wordreplace_find(word);
89         if (replace == NULL) {
90                 ret = NULL;
91                 g_string_free(result, TRUE);
92         } else {
93                 *pos = startpos+strlen(replace);
94
95                 g_string_insert(result, startpos, replace);
96                 ret = result->str;
97                 g_string_free(result, FALSE);
98         }
99
100         g_free(word);
101         return ret;
102 }
103
104 static void free_completions(void)
105 {
106         complist = g_list_first(complist);
107
108         g_list_foreach(complist, (GFunc) g_free, NULL);
109         g_list_free(complist);
110         complist = NULL;
111
112         g_free_and_null(last_line);
113 }
114
115 /* manual word completion - called when TAB is pressed */
116 char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase)
117 {
118         static int startpos = 0, wordlen = 0;
119         int old_startpos, old_wordlen;
120
121         GString *result;
122         char *word, *wordstart, *linestart, *ret;
123         int continue_complete, want_space;
124
125         g_return_val_if_fail(line != NULL, NULL);
126         g_return_val_if_fail(pos != NULL, NULL);
127
128         continue_complete = complist != NULL && *pos == last_line_pos &&
129                 strcmp(line, last_line) == 0;
130
131         old_startpos = startpos;
132         old_wordlen = wordlen;
133
134         if (!erase && continue_complete) {
135                 word = NULL;
136                 linestart = NULL;
137         } else {
138                 /* get the word we want to complete */
139                 word = get_word_at(line, *pos, &wordstart);
140                 startpos = (int) (wordstart-line);
141                 wordlen = strlen(word);
142
143                 /* get the start of line until the word we're completing */
144                 if (isseparator(*line)) {
145                         /* empty space at the start of line */
146                         if (wordstart == line)
147                                 wordstart += strlen(wordstart);
148                 } else {
149                         while (wordstart > line && isseparator(wordstart[-1]))
150                                 wordstart--;
151                 }
152                 linestart = g_strndup(line, (int) (wordstart-line));
153
154                 /* completions usually add space after the word, that makes
155                    things a bit harder. When continuing a completion
156                    "/msg nick1 "<tab> we have to cycle to nick2, etc.
157                    BUT if we start completion with "/msg "<tab>, we don't
158                    want to complete the /msg word, but instead complete empty
159                    word with /msg being in linestart. */
160                 if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
161                     (*linestart == '\0' || wordstart[-1] != ' ')) {
162                         char *old;
163
164                         old = linestart;
165                         linestart = *linestart == '\0' ?
166                                 g_strdup(word) :
167                                 g_strconcat(linestart, " ", word, NULL);
168                         g_free(old);
169
170                         g_free(word);
171                         word = g_strdup("");
172                         startpos = strlen(linestart)+1;
173                         wordlen = 0;
174                 }
175
176         }
177
178         if (erase) {
179                 signal_emit("complete erase", 3, window, word, linestart);
180
181                 if (!continue_complete)
182                         return NULL;
183
184                 /* jump to next completion */
185                 word = NULL;
186                 linestart = NULL;
187                 startpos = old_startpos;
188                 wordlen = old_wordlen;
189         }
190
191         if (continue_complete) {
192                 /* complete from old list */
193                 complist = complist->next != NULL ? complist->next :
194                         g_list_first(complist);
195                 want_space = last_want_space;
196         } else {
197                 /* get new completion list */
198                 free_completions();
199
200                 want_space = TRUE;
201                 signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
202                 last_want_space = want_space;
203         }
204
205         g_free(linestart);
206         g_free(word);
207
208         if (complist == NULL)
209                 return NULL;
210
211         /* word completed */
212         *pos = startpos+strlen(complist->data);
213
214         /* replace the word in line - we need to return
215            a full new line */
216         result = g_string_new(line);
217         g_string_erase(result, startpos, wordlen);
218         g_string_insert(result, startpos, complist->data);
219
220         if (want_space) {
221                 if (!isseparator(result->str[*pos]))
222                         g_string_insert_c(result, *pos, ' ');
223                 (*pos)++;
224         }
225
226         wordlen = strlen(complist->data);
227         last_line_pos = *pos;
228         g_free_not_null(last_line);
229         last_line = g_strdup(result->str);
230
231         ret = result->str;
232         g_string_free(result, FALSE);
233         return ret;
234 }
235
236 #define IS_CURRENT_DIR(dir) \
237         ((dir)[0] == '.' && ((dir)[1] == '\0' || (dir)[1] == G_DIR_SEPARATOR))
238
239 #define USE_DEFAULT_PATH(path, default_path) \
240         ((!g_path_is_absolute(path) || IS_CURRENT_DIR(path)) && \
241          default_path != NULL)
242
243 GList *list_add_file(GList *list, const char *name, const char *default_path)
244 {
245         struct stat statbuf;
246         char *fname;
247
248         g_return_val_if_fail(name != NULL, NULL);
249
250         fname = convert_home(name);
251         if (USE_DEFAULT_PATH(fname, default_path)) {
252                 g_free(fname);
253                 fname = g_strconcat(default_path, G_DIR_SEPARATOR_S,
254                                     name, NULL);
255         }
256         if (stat(fname, &statbuf) == 0) {
257                 list = g_list_append(list, !S_ISDIR(statbuf.st_mode) ? g_strdup(name) :
258                                      g_strconcat(name, G_DIR_SEPARATOR_S, NULL));
259         }
260
261         g_free(fname);
262         return list;
263 }
264
265 GList *filename_complete(const char *path, const char *default_path)
266 {
267         GList *list;
268         DIR *dirp;
269         struct dirent *dp;
270         char *realpath, *dir, *basename, *name;
271         int len;
272
273         g_return_val_if_fail(path != NULL, NULL);
274
275         list = NULL;
276
277         /* get directory part of the path - expand ~/ */
278         realpath = convert_home(path);
279         if (USE_DEFAULT_PATH(realpath, default_path)) {
280                 g_free(realpath);
281                 realpath = g_strconcat(default_path, G_DIR_SEPARATOR_S,
282                                        path, NULL);
283         }
284
285         /* open directory for reading */
286         dir = g_dirname(realpath);
287         dirp = opendir(dir);
288         g_free(dir);
289         g_free(realpath);
290
291         if (dirp == NULL)
292                 return NULL;
293
294         dir = g_dirname(path);
295         if (*dir == G_DIR_SEPARATOR && dir[1] == '\0') {
296                 /* completing file in root directory */
297                 *dir = '\0';
298         } else if (IS_CURRENT_DIR(dir) && !IS_CURRENT_DIR(path)) {
299                 /* completing file in default_path
300                    (path not set, and leave it that way) */
301                 g_free_and_null(dir);
302         }
303
304         basename = g_basename(path);
305         len = strlen(basename);
306
307         /* add all files in directory to completion list */
308         while ((dp = readdir(dirp)) != NULL) {
309                 if (dp->d_name[0] == '.') {
310                         if (dp->d_name[1] == '\0' ||
311                             (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
312                                 continue; /* skip . and .. */
313
314                         if (basename[0] != '.')
315                                 continue;
316                 }
317
318                 if (len == 0 || strncmp(dp->d_name, basename, len) == 0) {
319                         name = dir == NULL ? g_strdup(dp->d_name) :
320                                 g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name);
321                         list = list_add_file(list, name, default_path);
322                         g_free(name);
323                 }
324         }
325         closedir(dirp);
326
327         g_free_not_null(dir);
328         return list;
329 }
330
331 static GList *completion_get_settings(const char *key)
332 {
333         GList *complist;
334         GSList *tmp, *sets;
335         int len;
336
337         g_return_val_if_fail(key != NULL, NULL);
338
339         sets = settings_get_sorted();
340
341         len = strlen(key);
342         complist = NULL;
343         for (tmp = sets; tmp != NULL; tmp = tmp->next) {
344                 SETTINGS_REC *rec = tmp->data;
345
346                 if (g_strncasecmp(rec->key, key, len) == 0)
347                         complist = g_list_insert_sorted(complist, g_strdup(rec->key), (GCompareFunc) g_istr_cmp);
348         }
349         g_slist_free(sets);
350         return complist;
351 }
352
353 static GList *completion_get_bool_settings(const char *key)
354 {
355         GList *complist;
356         GSList *tmp, *sets;
357         int len;
358
359         g_return_val_if_fail(key != NULL, NULL);
360
361         sets = settings_get_sorted();
362
363         len = strlen(key);
364         complist = NULL;
365         for (tmp = sets; tmp != NULL; tmp = tmp->next) {
366                 SETTINGS_REC *rec = tmp->data;
367
368                 if (rec->type == SETTING_TYPE_BOOLEAN &&
369                     g_strncasecmp(rec->key, key, len) == 0)
370                         complist = g_list_insert_sorted(complist, g_strdup(rec->key), (GCompareFunc) g_istr_cmp);
371         }
372         g_slist_free(sets);
373         return complist;
374 }
375
376 static GList *completion_get_aliases(const char *alias, char cmdchar)
377 {
378         CONFIG_NODE *node;
379         GList *complist;
380         GSList *tmp;
381         char *word;
382         int len;
383
384         g_return_val_if_fail(alias != NULL, NULL);
385
386         /* get list of aliases from mainconfig */
387         node = iconfig_node_traverse("aliases", FALSE);
388         tmp = node == NULL ? NULL : config_node_first(node->value);
389
390         len = strlen(alias);
391         complist = NULL;
392         for (; tmp != NULL; tmp = config_node_next(tmp)) {
393                 CONFIG_NODE *node = tmp->data;
394
395                 if (node->type != NODE_TYPE_KEY)
396                         continue;
397
398                 if (g_strncasecmp(node->key, alias, len) == 0) {
399                         word = g_strdup_printf("%c%s", cmdchar, node->key);
400                         /* add matching alias to completion list, aliases will
401                            be appended after command completions and kept in
402                            uppercase to show it's an alias */
403                         if (glist_find_icase_string(complist, word) == NULL)
404                                 complist = g_list_insert_sorted(complist, word, (GCompareFunc) g_istr_cmp);
405                         else
406                                 g_free(word);
407                 }
408         }
409         return complist;
410 }
411
412 static GList *completion_get_commands(const char *cmd, char cmdchar)
413 {
414         GList *complist;
415         GSList *tmp;
416         char *word;
417         int len;
418
419         g_return_val_if_fail(cmd != NULL, NULL);
420
421         len = strlen(cmd);
422         complist = NULL;
423         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
424                 COMMAND_REC *rec = tmp->data;
425
426                 if (strchr(rec->cmd, ' ') != NULL)
427                         continue;
428
429                 if (g_strncasecmp(rec->cmd, cmd, len) == 0) {
430                         word = cmdchar == '\0' ? g_strdup(rec->cmd) :
431                                 g_strdup_printf("%c%s", cmdchar, rec->cmd);
432                         if (glist_find_icase_string(complist, word) == NULL)
433                                 complist = g_list_insert_sorted(complist, word, (GCompareFunc) g_istr_cmp);
434                         else
435                                 g_free(word);
436                 }
437         }
438         return complist;
439 }
440
441 static GList *completion_get_subcommands(const char *cmd)
442 {
443         GList *complist;
444         GSList *tmp;
445         char *spacepos;
446         int len, skip;
447
448         g_return_val_if_fail(cmd != NULL, NULL);
449
450         /* get the number of chars to skip at the start of command. */
451         spacepos = strrchr(cmd, ' ');
452         skip = spacepos == NULL ? strlen(cmd)+1 :
453                 ((int) (spacepos-cmd) + 1);
454
455         len = strlen(cmd);
456         complist = NULL;
457         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
458                 COMMAND_REC *rec = tmp->data;
459
460                 if ((int)strlen(rec->cmd) < len)
461                         continue;
462
463                 if (strchr(rec->cmd+len, ' ') != NULL)
464                         continue;
465
466                 if (g_strncasecmp(rec->cmd, cmd, len) == 0)
467                         complist = g_list_insert_sorted(complist, g_strdup(rec->cmd+skip), (GCompareFunc) g_istr_cmp);
468         }
469         return complist;
470 }
471
472 GList *completion_get_options(const char *cmd, const char *option)
473 {
474         COMMAND_REC *rec;
475         GList *list;
476         char **tmp;
477         int len;
478
479         g_return_val_if_fail(cmd != NULL, NULL);
480         g_return_val_if_fail(option != NULL, NULL);
481
482         rec = command_find(cmd);
483         if (rec == NULL || rec->options == NULL) return NULL;
484
485         list = NULL;
486         len = strlen(option);
487         for (tmp = rec->options; *tmp != NULL; tmp++) {
488                 const char *optname = *tmp + iscmdtype(**tmp);
489
490                 if (len == 0 || g_strncasecmp(optname, option, len) == 0)
491                         list = g_list_append(list, g_strconcat("-", optname, NULL));
492         }
493
494         return list;
495 }
496
497 /* split the line to command and arguments */
498 static char *line_get_command(const char *line, char **args, int aliases)
499 {
500         const char *ptr, *cmdargs;
501         char *cmd, *checkcmd;
502
503         g_return_val_if_fail(line != NULL, NULL);
504         g_return_val_if_fail(args != NULL, NULL);
505
506         cmd = checkcmd = NULL; *args = "";
507         cmdargs = NULL; ptr = line;
508
509         do {
510                 ptr = strchr(ptr, ' ');
511                 if (ptr == NULL) {
512                         checkcmd = g_strdup(line);
513                         cmdargs = "";
514                 } else {
515                         checkcmd = g_strndup(line, (int) (ptr-line));
516
517                         while (i_isspace(*ptr)) ptr++;
518                         cmdargs = ptr;
519                 }
520
521                 if (aliases ? !alias_find(checkcmd) :
522                     !command_find(checkcmd)) {
523                         /* not found, use the previous */
524                         g_free(checkcmd);
525                         break;
526                 }
527
528                 /* found, check if it has subcommands */
529                 g_free_not_null(cmd);
530                 if (!aliases)
531                         cmd = checkcmd;
532                 else {
533                         cmd = g_strdup(alias_find(checkcmd));
534                         g_free(checkcmd);
535                 }
536                 *args = (char *) cmdargs;
537         } while (ptr != NULL);
538
539         if (cmd != NULL)
540                 g_strdown(cmd);
541         return cmd;
542 }
543
544 static char *expand_aliases(const char *line)
545 {
546         char *cmd, *args, *ret;
547
548         g_return_val_if_fail(line != NULL, NULL);
549
550         cmd = line_get_command(line, &args, TRUE);
551         if (cmd == NULL) return g_strdup(line);
552         if (*args == '\0') return cmd;
553
554         ret = g_strconcat(cmd, " ", args, NULL);
555         g_free(cmd);
556         return ret;
557 }
558
559 static void sig_complete_word(GList **list, WINDOW_REC *window,
560                               const char *word, const char *linestart,
561                               int *want_space)
562 {
563         const char *newword, *cmdchars;
564         char *signal, *cmd, *args, *line;
565
566         g_return_if_fail(list != NULL);
567         g_return_if_fail(word != NULL);
568         g_return_if_fail(linestart != NULL);
569
570         /* check against "completion words" list */
571         newword = completion_find(word);
572         if (newword != NULL) {
573                 *list = g_list_append(*list, g_strdup(newword));
574
575                 signal_stop();
576                 return;
577         }
578
579         /* command completion? */
580         cmdchars = settings_get_str("cmdchars");
581         if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) {
582                 /* complete /command */
583                 *list = completion_get_commands(word+1, *word);
584
585                 /* complete aliases, too */
586                 *list = g_list_concat(*list,
587                                       completion_get_aliases(word+1, *word));
588
589                 if (*list != NULL) signal_stop();
590                 return;
591         }
592
593         /* check only for /command completions from now on */
594         if (*linestart == '\0')
595                 return;
596
597         cmdchars = strchr(cmdchars, *linestart);
598         if (cmdchars == NULL) return;
599
600         /* check if there's aliases */
601         line = linestart[1] == *cmdchars ? g_strdup(linestart+2) :
602                 expand_aliases(linestart+1);
603
604         cmd = line_get_command(line, &args, FALSE);
605         if (cmd == NULL) {
606                 g_free(line);
607                 return;
608         }
609
610         /* we're completing -option? */
611         if (*word == '-') {
612                 *list = completion_get_options(cmd, word+1);
613                 g_free(cmd);
614                 g_free(line);
615                 return;
616         }
617
618         /* complete parameters */
619         signal = g_strconcat("complete command ", cmd, NULL);
620         signal_emit(signal, 5, list, window, word, args, want_space);
621
622         if (command_have_sub(line)) {
623                 /* complete subcommand */
624                 g_free(cmd);
625                 cmd = g_strconcat(line, " ", word, NULL);
626                 *list = g_list_concat(completion_get_subcommands(cmd), *list);
627
628                 if (*list != NULL) signal_stop();
629         }
630
631         g_free(signal);
632         g_free(cmd);
633
634         g_free(line);
635 }
636
637 static void sig_complete_erase(WINDOW_REC *window, const char *word,
638                                const char *linestart)
639 {
640         const char *cmdchars;
641         char *line, *cmd, *args, *signal;
642
643         if (*linestart == '\0')
644                 return;
645
646         /* we only want to check for commands */
647         cmdchars = settings_get_str("cmdchars");
648         cmdchars = strchr(cmdchars, *linestart);
649         if (cmdchars == NULL)
650                 return;
651
652         /* check if there's aliases */
653         line = linestart[1] == *cmdchars ? g_strdup(linestart+2) :
654                 expand_aliases(linestart+1);
655
656         cmd = line_get_command(line, &args, FALSE);
657         if (cmd == NULL) {
658                 g_free(line);
659                 return;
660         }
661
662         signal = g_strconcat("complete erase command ", cmd, NULL);
663         signal_emit(signal, 3, window, word, args);
664
665         g_free(signal);
666         g_free(cmd);
667         g_free(line);
668 }
669
670 static void sig_complete_set(GList **list, WINDOW_REC *window,
671                              const char *word, const char *line, int *want_space)
672 {
673         g_return_if_fail(list != NULL);
674         g_return_if_fail(word != NULL);
675         g_return_if_fail(line != NULL);
676
677         if (*line != '\0') return;
678
679         *list = completion_get_settings(word);
680         if (*list != NULL) signal_stop();
681 }
682
683 static void sig_complete_toggle(GList **list, WINDOW_REC *window,
684                                 const char *word, const char *line, int *want_space)
685 {
686         g_return_if_fail(list != NULL);
687         g_return_if_fail(word != NULL);
688         g_return_if_fail(line != NULL);
689
690         if (*line != '\0') return;
691
692         *list = completion_get_bool_settings(word);
693         if (*list != NULL) signal_stop();
694 }
695
696 /* first argument of command is file name - complete it */
697 static void sig_complete_filename(GList **list, WINDOW_REC *window,
698                                   const char *word, const char *line, int *want_space)
699 {
700         g_return_if_fail(list != NULL);
701         g_return_if_fail(word != NULL);
702         g_return_if_fail(line != NULL);
703
704         if (*line != '\0') return;
705
706         *list = filename_complete(word, NULL);
707         if (*list != NULL) {
708                 *want_space = FALSE;
709                 signal_stop();
710         }
711 }
712
713 /* first argument of command is .. command :) (/HELP command) */
714 static void sig_complete_command(GList **list, WINDOW_REC *window,
715                                   const char *word, const char *line, int *want_space)
716 {
717         char *cmd;
718
719         g_return_if_fail(list != NULL);
720         g_return_if_fail(word != NULL);
721         g_return_if_fail(line != NULL);
722
723         if (*line == '\0') {
724                 /* complete base command */
725                 *list = completion_get_commands(word, '\0');
726         } else if (command_have_sub(line)) {
727                 /* complete subcommand */
728                 cmd = g_strconcat(line, " ", word, NULL);
729                 *list = completion_get_subcommands(cmd);
730                 g_free(cmd);
731         }
732
733         if (*list != NULL) signal_stop();
734 }
735
736 void completion_init(void)
737 {
738         complist = NULL;
739         last_line = NULL; last_line_pos = -1;
740
741         chat_completion_init();
742
743         signal_add_first("complete word", (SIGNAL_FUNC) sig_complete_word);
744         signal_add_first("complete erase", (SIGNAL_FUNC) sig_complete_erase);
745         signal_add("complete command set", (SIGNAL_FUNC) sig_complete_set);
746         signal_add("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
747         signal_add("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
748         signal_add("complete command save", (SIGNAL_FUNC) sig_complete_filename);
749         signal_add("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
750         signal_add("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
751         signal_add("complete command rawlog save", (SIGNAL_FUNC) sig_complete_filename);
752         signal_add("complete command help", (SIGNAL_FUNC) sig_complete_command);
753 }
754
755 void completion_deinit(void)
756 {
757         free_completions();
758
759         chat_completion_deinit();
760
761         signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
762         signal_remove("complete erase", (SIGNAL_FUNC) sig_complete_erase);
763         signal_remove("complete command set", (SIGNAL_FUNC) sig_complete_set);
764         signal_remove("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
765         signal_remove("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
766         signal_remove("complete command save", (SIGNAL_FUNC) sig_complete_filename);
767         signal_remove("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
768         signal_remove("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
769         signal_remove("complete command rawlog save", (SIGNAL_FUNC) sig_complete_filename);
770         signal_remove("complete command help", (SIGNAL_FUNC) sig_complete_command);
771 }
772
773