From 04d848e6d9b5b34667f2e17891caf183822f7870 Mon Sep 17 00:00:00 2001 From: Chris Allegretta Date: Sun, 5 Nov 2000 17:54:41 +0000 Subject: [PATCH] Added tab completion code git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@267 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 7 +- files.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++ po/nano.pot | 70 +++++++++---------- proto.h | 4 +- utils.c | 100 +++++++++++++++++++++++++++ winio.c | 22 +++++- 6 files changed, 354 insertions(+), 41 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9c7e0c54..1390d27d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,6 +19,9 @@ CVS Code - New functions search_init_globals and do_replace_loop, changes to search_init(), do_replace, findnextstr, moved last_search and last_replace back to nano.c (*shrug*). + - New tab completion code. Used check_wildcard_match, input_tab, + cwd_tab_completion, username_tab_completion from busybox, + hacked them a lot, changes to nanogetstr(). - files.c: do_writeout() - Change strcpy to answer to mallocstrcpy. @@ -59,8 +62,8 @@ CVS Code - into statusbar" bug in odd $TERMs like iris-ansi. - Changed check to return -2 on "enter" from answer == "" to answer == def. - - Silly kludge in backspace code to temporarily decrement x - before refreshing to keep the cursor visually aligned. + - Fixed fallthrough code because there was no break. Make much + more sense now. nanoget_repaint() - New function, removes about 30 lines of duplicate code in nanogetstr(). diff --git a/files.c b/files.c index 6b871e37..c6279d70 100644 --- a/files.c +++ b/files.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "config.h" #include "proto.h" @@ -494,3 +496,193 @@ int do_writeout_void(void) { return do_writeout(0); } + + +/* + * These functions (username_tab_completion, cwd_tab_completion, and + * input_tab were taken from busybox 0.46 (cmdedit.c). Here is the notice + * from that file: + * + * Termios command line History and Editting, originally + * intended for NetBSD sh (ash) + * Copyright (c) 1999 + * Main code: Adam Rogoyski + * Etc: Dave Cinege + * Majorly adjusted/re-written for busybox: + * Erik Andersen + * + * You may use this code as you wish, so long as the original author(s) + * are attributed in any redistributions of the source code. + * This code is 'as is' with no warranty. + * This code may safely be consumed by a BSD or GPL license. + */ + +char **username_tab_completion(char *buf, int *num_matches) +{ + char **matches = (char **) NULL; + *num_matches = 0; +#ifdef DEBUG + fprintf(stderr, "\nin username_tab_completion\n"); +#endif + return (matches); +} + +/* This was originally called exe_n_cwd_tab_completion, but we're not + worried about executables, only filenames :> */ + +char **cwd_tab_completion(char *buf, int *num_matches) +{ + char *dirName, *tmp = NULL; + char **matches = (char **) NULL; + DIR *dir; + struct dirent *next; + + matches = malloc(sizeof(char *) * 50); + + /* Stick a wildcard onto the buf, for later use */ + strcat(buf, "*"); + + /* Now wall the current directory */ +/* if (!strcmp(filename, "")) + dirName = get_current_dir_name(); */ + + if (strcmp(buf, "") && strstr(buf, "/")) { + dirName = malloc(strlen(buf) + 1); + tmp = buf + strlen(buf); + while (*tmp != '/' && tmp != buf) + tmp--; + strncpy(dirName, buf, tmp - buf); + dirName[tmp - buf] = 0; + tmp++; + + } else { + if ((dirName = getcwd(NULL, 0)) == NULL) + return matches; + else + tmp = buf; + } + +#ifdef DEBUG + fprintf(stderr, "\nDir = %s\n", dirName); + fprintf(stderr, "\nbuf = %s\n", buf); + fprintf(stderr, "\ntmp = %s\n", tmp); +#endif + + dir = opendir(dirName); + if (!dir) { + /* Don't print an error, just shut up and return */ + *num_matches = 0; + beep(); + return (matches); + } + while ((next = readdir(dir)) != NULL) { + + /* Some quick sanity checks */ + if ((strcmp(next->d_name, "..") == 0) + || (strcmp(next->d_name, ".") == 0)) { + continue; + } +#ifdef DEBUG + fprintf(stderr, "\nComparing \'%s\'\n", next->d_name); +#endif + /* See if this matches */ + if (check_wildcard_match(next->d_name, tmp) == TRUE) { + /* Cool, found a match. Add it to the list */ + matches[*num_matches] = malloc(strlen(next->d_name) + 1); + strcpy(matches[*num_matches], next->d_name); + ++*num_matches; + //matches = realloc( matches, sizeof(char*)*(*num_matches)); + } + } + + return (matches); +} + +/* This function now return an int which refers to how much the + * statusbar (place) should be advanced, i.e. the new cursor pos. + */ +int input_tab(char *buf, int place, int lastWasTab) +{ + /* Do TAB completion */ + static int num_matches = 0; + static char **matches = (char **) NULL; + int pos = place, newplace = 0; + + if (lastWasTab == FALSE) { + char *tmp, *matchBuf; + + /* For now, we will not bother with trying to distinguish + * whether the cursor is in/at a command extression -- we + * will always try all possible matches. If you don't like + * that then feel free to fix it. + */ + + /* Make a local copy of the string -- up to the position of the + cursor */ + matchBuf = (char *) calloc(strlen(buf), sizeof(char)); + strncpy(matchBuf, buf, place); + tmp = matchBuf; + + /* skip any leading white space */ + while (*tmp && isspace(*tmp)) + ++tmp; + + /* Free up any memory already allocated */ + if (matches != NULL) { + free(matches); + matches = (char **) NULL; + } + + /* If the word starts with `~' and there is no slash in the word, + * then try completing this word as a username. */ + + /* FIXME -- this check is broken! + if (*tmp == '~' && !strchr(tmp, '/')) + matches = username_tab_completion(tmp, &num_matches); */ + + /* Try to match everything in the current working directory that + * matches. */ + if (!matches) + matches = cwd_tab_completion(tmp, &num_matches); + + /* Don't leak memory */ + free(matchBuf); + + /* Did we find exactly one match? */ + if (matches && num_matches == 1) { + buf = nrealloc(buf, strlen(buf) + strlen(matches[0]) - pos + 1); + /* write out the matched command */ + strncpy(buf + pos, matches[0] + pos, + strlen(matches[0]) - pos); + newplace += strlen(matches[0]) - pos; + } + } else { + /* Ok -- the last char was a TAB. Since they + * just hit TAB again, print a list of all the + * available choices... */ + if (matches && num_matches > 0) { + int i, col; + + /* Blank the edit window, and print the matches out there */ + blank_edit(); + wmove(edit, 0, 0); + + /* Print the list of matches */ + for (i = 0, col = 0; i < num_matches; i++) { + char foo[17]; + sprintf(foo, "%-14s ", matches[i]); + col += waddnstr(edit, foo, strlen(foo)); + if (col > 60 && matches[i + 1] != NULL) { + waddstr(edit, "\n"); + col = 0; + } + } + wrefresh(edit); + num_matches = 0; + } + + } + + edit_refresh(); + return newplace; +} diff --git a/po/nano.pot b/po/nano.pot index 914d7545..95853cbd 100644 --- a/po/nano.pot +++ b/po/nano.pot @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2000-11-05 11:52-0500\n" +"POT-Creation-Date: 2000-11-05 12:08-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,87 +23,87 @@ msgstr "" msgid "Blew away cutbuffer =)\n" msgstr "" -#: files.c:120 +#: files.c:122 msgid "read_line: not on first line and prev is NULL" msgstr "" -#: files.c:182 files.c:199 +#: files.c:184 files.c:201 #, c-format msgid "Read %d lines" msgstr "" -#: files.c:217 search.c:164 +#: files.c:219 search.c:164 #, c-format msgid "\"%s\" not found" msgstr "" #. We have a new file -#: files.c:221 +#: files.c:223 msgid "New File" msgstr "" -#: files.c:230 +#: files.c:232 #, c-format msgid "File \"%s\" is a directory" msgstr "" -#: files.c:236 +#: files.c:238 msgid "Reading File" msgstr "" -#: files.c:249 +#: files.c:251 msgid "File to insert [from ./] " msgstr "" -#: files.c:274 files.c:298 files.c:486 nano.c:1347 +#: files.c:276 files.c:300 files.c:488 nano.c:1347 msgid "Cancelled" msgstr "" -#: files.c:320 files.c:340 files.c:354 files.c:371 files.c:377 +#: files.c:322 files.c:342 files.c:356 files.c:373 files.c:379 #, c-format msgid "Could not open file for writing: %s" msgstr "" -#: files.c:328 +#: files.c:330 msgid "Could not open file: Path length exceeded." msgstr "" -#: files.c:359 +#: files.c:361 #, c-format msgid "Wrote >%s\n" msgstr "" -#: files.c:386 +#: files.c:388 #, c-format msgid "Could not close %s: %s" msgstr "" #. Try a rename?? -#: files.c:407 files.c:418 files.c:423 +#: files.c:409 files.c:420 files.c:425 #, c-format msgid "Could not open %s for writing: %s" msgstr "" -#: files.c:429 +#: files.c:431 #, c-format msgid "Could not set permissions %o on %s: %s" msgstr "" -#: files.c:436 +#: files.c:438 #, c-format msgid "Wrote %d lines" msgstr "" -#: files.c:465 +#: files.c:467 msgid "File Name to write" msgstr "" -#: files.c:470 +#: files.c:472 #, c-format msgid "filename is %s" msgstr "" -#: files.c:475 +#: files.c:477 msgid "File exists, OVERWRITE ?" msgstr "" @@ -380,7 +380,7 @@ msgid "Case Sens" msgstr "" #: global.c:344 global.c:364 global.c:375 global.c:385 global.c:401 -#: global.c:405 global.c:411 winio.c:993 +#: global.c:405 global.c:411 winio.c:1009 msgid "Cancel" msgstr "" @@ -817,67 +817,67 @@ msgstr "" msgid "actual_x_from_start for xplus=%d returned %d\n" msgstr "" -#: winio.c:408 +#: winio.c:424 #, c-format msgid "input '%c' (%d)\n" msgstr "" -#: winio.c:446 +#: winio.c:462 msgid "New Buffer" msgstr "" -#: winio.c:449 +#: winio.c:465 msgid " File: ..." msgstr "" -#: winio.c:457 +#: winio.c:473 msgid "Modified" msgstr "" -#: winio.c:909 +#: winio.c:925 #, c-format msgid "Moved to (%d, %d) in edit buffer\n" msgstr "" -#: winio.c:920 +#: winio.c:936 #, c-format msgid "current->data = \"%s\"\n" msgstr "" -#: winio.c:963 +#: winio.c:979 #, c-format msgid "I got \"%s\"\n" msgstr "" -#: winio.c:988 +#: winio.c:1004 msgid "Yes" msgstr "" -#: winio.c:990 +#: winio.c:1006 msgid "All" msgstr "" -#: winio.c:992 +#: winio.c:1008 msgid "No" msgstr "" -#: winio.c:1129 +#: winio.c:1145 #, c-format msgid "do_cursorpos: linepct = %f, bytepct = %f\n" msgstr "" -#: winio.c:1133 +#: winio.c:1149 msgid "line %d of %d (%.0f%%), character %d of %d (%.0f%%)" msgstr "" -#: winio.c:1261 +#: winio.c:1277 msgid "Dumping file buffer to stderr...\n" msgstr "" -#: winio.c:1263 +#: winio.c:1279 msgid "Dumping cutbuffer to stderr...\n" msgstr "" -#: winio.c:1265 +#: winio.c:1281 msgid "Dumping a buffer to stderr...\n" msgstr "" diff --git a/proto.h b/proto.h index be689f0e..cf7a5d8a 100644 --- a/proto.h +++ b/proto.h @@ -87,7 +87,8 @@ int do_up(void); int do_down(void); int do_left(void); int do_right(void); - +int check_wildcard_match(const char *text, const char *pattern); +int input_tab(char *buf, int place, int lastWasTab); void shortcut_init(void); void lowercase(char *src); @@ -125,6 +126,7 @@ void new_magicline(void); void splice_node(filestruct *begin, filestruct *new, filestruct *end); void null_at(char *data, int index); void page_up_center(void); +void blank_edit(void); void search_init_globals(void); void replace_abort(void); diff --git a/utils.c b/utils.c index f538de7a..4b248053 100644 --- a/utils.c +++ b/utils.c @@ -148,3 +148,103 @@ void new_magicline(void) filebot = filebot->next; totlines++; } + +/* + * Routine to see if a text string is matched by a wildcard pattern. + * Returns TRUE if the text is matched, or FALSE if it is not matched + * or if the pattern is invalid. + * * matches zero or more characters + * ? matches a single character + * [abc] matches 'a', 'b' or 'c' + * \c quotes character c + * Adapted from code written by Ingo Wilken, and + * then taken from sash, Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * Permission to distribute this code under the GPL has been granted. + */ +int check_wildcard_match(const char *text, const char *pattern) +{ + const char *retryPat; + const char *retryText; + int ch; + int found; + int len; + + retryPat = NULL; + retryText = NULL; + + while (*text || *pattern) { + ch = *pattern++; + + switch (ch) { + case '*': + retryPat = pattern; + retryText = text; + break; + + case '[': + found = FALSE; + + while ((ch = *pattern++) != ']') { + if (ch == '\\') + ch = *pattern++; + + if (ch == '\0') + return FALSE; + + if (*text == ch) + found = TRUE; + } + len = strlen(text); + if (found == FALSE && len != 0) { + return FALSE; + } + if (found == TRUE) { + if (strlen(pattern) == 0 && len == 1) { + return TRUE; + } + if (len != 0) { + text++; + continue; + } + } + + /* fall into next case */ + + case '?': + if (*text++ == '\0') + return FALSE; + + break; + + case '\\': + ch = *pattern++; + + if (ch == '\0') + return FALSE; + + /* fall into next case */ + + default: + if (*text == ch) { + if (*text) + text++; + break; + } + + if (*text) { + pattern = retryPat; + text = ++retryText; + break; + } + + return FALSE; + } + + if (pattern == NULL) + return FALSE; + } + + return TRUE; +} diff --git a/winio.c b/winio.c index ab6c511d..5a200502 100644 --- a/winio.c +++ b/winio.c @@ -247,7 +247,7 @@ void nanoget_repaint(char *buf, char *inputbuf, int x) int nanogetstr(char *buf, char *def, shortcut s[], int slen, int start_x) { int kbinput = 0, j = 0, x = 0, xend; - int x_left = 0, inputlen; + int x_left = 0, inputlen, tabbed = 0; char *inputbuf; inputbuf = nmalloc(strlen(def) + 1); @@ -272,6 +272,9 @@ int nanogetstr(char *buf, char *def, shortcut s[], int slen, int start_x) } xend = strlen(buf) + strlen(inputbuf); + if (kbinput != '\t') + tabbed = 0; + switch (kbinput) { /* Stuff we want to equate with , ASCII 13 */ case 343: @@ -321,9 +324,22 @@ int nanogetstr(char *buf, char *def, shortcut s[], int slen, int start_x) inputbuf[strlen(inputbuf) - 1] = 0; } } - x--; + if (x > strlen(buf)) + x--; nanoget_repaint(buf, inputbuf, x); - x++; + break; + case NANO_CONTROL_I: + tabbed++; +#ifdef DEBUG + fprintf(stderr, "Before call, x = %d\n", x); +#endif + x += input_tab(inputbuf, (x - x_left), tabbed - 1); +#ifdef DEBUG + fprintf(stderr, "After call, x = %d\n", x); +#endif + nanoget_repaint(buf, inputbuf, x); + tabbed = 1; + break; case KEY_LEFT: if (x > strlen(buf)) x--;