commit 65e6ecb1d47a6167649a647e813cc0302673875c
parent daa533a767104223fe264cc4c2085079ff3c56a9
Author: David Lawrence Ramsey <pooka109@gmail.com>
Date: Tue, 8 Feb 2005 20:37:53 +0000
add DB's pverhaul the tab completion code and a few related functions to
increase efficiency and support multibyte characters; also add a few
miscellaneous tweaks of mine
git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2309 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
Diffstat:
8 files changed, 331 insertions(+), 503 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -149,6 +149,15 @@ CVS code -
get the value of totsize in a few more places. Changes to
read_line(), read_file(), do_delete(), do_input(),
get_totals(), and do_cursorpos(). (DLR)
+ - Overhaul the tab completion code and a few related functions
+ to increase efficiency and support multibyte characters. New
+ functions strrchrn() and is_dir(); changes to diralphasort(),
+ username_tab_completion(), cwd_tab_completion(), input_tab(),
+ tail(), and striponedir(); removal of append_slash_if_dir()
+ and check_wildcard_match(). (David Benbennick) DLR: Move the
+ routine to get the current user's home directory into the new
+ function get_homedir(), and use it where necessary. Also add
+ a few miscellaneous tweaks.
- cut.c:
do_cut_text()
- If keep_cutbuffer is FALSE, only blow away the text in the
diff --git a/src/chars.c b/src/chars.c
@@ -41,13 +41,13 @@
/* Return TRUE if the value of c is in byte range, and FALSE
* otherwise. */
-bool is_byte(unsigned int c)
+bool is_byte(int c)
{
- return (c == (unsigned char)c);
+ return ((unsigned int)c == (unsigned char)c);
}
/* This function is equivalent to isalnum(). */
-bool is_alnum_char(unsigned int c)
+bool is_alnum_char(int c)
{
return isalnum(c);
}
@@ -82,7 +82,7 @@ bool is_alnum_wchar(wchar_t wc)
#endif
/* This function is equivalent to isblank(). */
-bool is_blank_char(unsigned int c)
+bool is_blank_char(int c)
{
return
#ifdef HAVE_ISBLANK
@@ -130,9 +130,10 @@ bool is_blank_wchar(wchar_t wc)
/* This function is equivalent to iscntrl(), except in that it also
* handles control characters with their high bits set. */
-bool is_cntrl_char(unsigned int c)
+bool is_cntrl_char(int c)
{
- return (0 <= c && c < 32) || (127 <= c && c < 160);
+ return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
+ (127 <= c && c < 160);
}
/* This function is equivalent to iscntrl() for multibyte characters,
@@ -273,7 +274,7 @@ int mb_cur_max(void)
/* Convert the value in chr to a multibyte character with the same
* wide character value as chr. Return the multibyte character and its
* length. */
-char *make_mbchar(unsigned int chr, char *chr_mb, int *chr_mb_len)
+char *make_mbchar(int chr, char *chr_mb, int *chr_mb_len)
{
assert(chr_mb != NULL && chr_mb_len != NULL);
@@ -412,7 +413,7 @@ size_t move_mbleft(const char *buf, size_t pos)
#endif
, NULL);
- if (pos_prev <= buf_mb_len)
+ if (pos_prev <= (size_t)buf_mb_len)
break;
pos_prev -= buf_mb_len;
@@ -760,3 +761,17 @@ size_t mbstrnlen(const char *s, size_t maxlen)
nstrnlen(s, maxlen);
#endif
}
+
+/* Find the one-based position of the last occurrence of character c in
+ * the first n characters of s. Return 0 if c is not found. */
+size_t strrchrn(const char *s, int c, size_t n)
+{
+ assert(n <= strlen(s));
+
+ for (s += n - 1; n >= 1; n--, s--) {
+ if (c == *s)
+ return n;
+ }
+
+ return 0;
+}
diff --git a/src/files.c b/src/files.c
@@ -1911,6 +1911,7 @@ int do_writeout(bool exiting)
} /* while (TRUE) */
free(ans);
+
return retval;
}
@@ -1920,71 +1921,96 @@ void do_writeout_void(void)
display_main_list();
}
-/* Return a malloc()ed string containing the actual directory, used
- * to convert ~user and ~/ notation... */
+/* Return a malloc()ed string containing the actual directory, used to
+ * convert ~user/ and ~/ notation. */
char *real_dir_from_tilde(const char *buf)
{
char *dirtmp = NULL;
+ if (buf == NULL)
+ return NULL;
+
if (buf[0] == '~') {
size_t i;
- const struct passwd *userdata;
+ const char *tilde_dir;
- /* Figure how how much of the str we need to compare */
+ /* Figure out how much of the str we need to compare. */
for (i = 1; buf[i] != '/' && buf[i] != '\0'; i++)
;
- /* Determine home directory using getpwuid() or getpwent(),
- don't rely on $HOME */
- if (i == 1)
- userdata = getpwuid(geteuid());
- else {
+ /* Get the home directory. */
+ if (i == 1) {
+ get_homedir();
+ tilde_dir = homedir;
+ } else {
+ const struct passwd *userdata;
+
do {
userdata = getpwent();
} while (userdata != NULL &&
strncmp(userdata->pw_name, buf + 1, i - 1) != 0);
+ endpwent();
+ tilde_dir = userdata->pw_dir;
}
- endpwent();
- if (userdata != NULL) { /* User found */
- dirtmp = charalloc(strlen(userdata->pw_dir) +
- strlen(buf + i) + 1);
- sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);
+ if (tilde_dir != NULL) {
+ dirtmp = charalloc(strlen(tilde_dir) + strlen(buf + i) + 1);
+ sprintf(dirtmp, "%s%s", tilde_dir, buf + i);
}
}
+ /* Set a default value for dirtmp, in case the user's home directory
+ * isn't found. */
if (dirtmp == NULL)
- dirtmp = mallocstrcpy(dirtmp, buf);
+ dirtmp = mallocstrcpy(NULL, buf);
return dirtmp;
}
+#if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER)
+/* Our sort routine for file listings. Sort directories before
+ * filenames, alphabetically and ignoring case differences. Sort
+ * filenames the same way, except for ignoring an initial dot. */
+int diralphasort(const void *va, const void *vb)
+{
+ struct stat fileinfo;
+ const char *a = *(const char *const *)va;
+ const char *b = *(const char *const *)vb;
+ bool aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
+ bool bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
+
+ if (aisdir && !bisdir)
+ return -1;
+ if (!aisdir && bisdir)
+ return 1;
+
+ if (*a == '.')
+ a++;
+ if (*b == '.')
+ b++;
+
+ return strcasecmp(a, b);
+}
+#endif
+
#ifndef DISABLE_TABCOMP
-/* Tack a slash onto the string we're completing if it's a directory.
- * We assume there is room for one more character on the end of buf.
- * The return value says whether buf is a directory. */
-int append_slash_if_dir(char *buf, bool *lastwastab, int *place)
+/* Is the given file a directory? */
+int is_dir(const char *buf)
{
char *dirptr = real_dir_from_tilde(buf);
struct stat fileinfo;
- int ret = 0;
- assert(dirptr != buf);
+ int ret = (stat(dirptr, &fileinfo) != -1 &&
+ S_ISDIR(fileinfo.st_mode));
- if (stat(dirptr, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode)) {
- strncat(buf, "/", 1);
- (*place)++;
- /* now we start over again with # of tabs so far */
- *lastwastab = FALSE;
- ret = 1;
- }
+ assert(buf != NULL && dirptr != buf);
free(dirptr);
+
return ret;
}
-/*
- * These functions (username_tab_completion(), cwd_tab_completion(), and
+/* 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:
*
@@ -1999,46 +2025,38 @@ int append_slash_if_dir(char *buf, bool *lastwastab, int *place)
* 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.
- */
+ * This code may safely be consumed by a BSD or GPL license. */
-char **username_tab_completion(char *buf, int *num_matches)
+/* We consider the first buflen characters of buf for ~username tab
+ * completion. */
+char **username_tab_completion(const char *buf, size_t *num_matches,
+ size_t buflen)
{
- char **matches = (char **)NULL;
- char *matchline = NULL;
- struct passwd *userdata;
+ char **matches = NULL;
+ const struct passwd *userdata;
- *num_matches = 0;
- matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
+ assert(buf != NULL && num_matches != NULL && buflen > 0);
- strcat(buf, "*");
+ *num_matches = 0;
while ((userdata = getpwent()) != NULL) {
-
- if (check_wildcard_match(userdata->pw_name, &buf[1])) {
-
- /* Cool, found a match. Add it to the list
- * This makes a lot more sense to me (Chris) this way...
- */
+ if (strncmp(userdata->pw_name, buf + 1, buflen - 1) == 0) {
+ /* Cool, found a match. Add it to the list. This makes a
+ * lot more sense to me (Chris) this way... */
#ifndef DISABLE_OPERATINGDIR
/* ...unless the match exists outside the operating
- directory, in which case just go to the next match */
-
- if (operating_dir != NULL) {
- if (check_operating_dir(userdata->pw_dir, TRUE) != 0)
- continue;
- }
+ * directory, in which case just go to the next match. */
+ if (check_operating_dir(userdata->pw_dir, TRUE))
+ continue;
#endif
- matchline = charalloc(strlen(userdata->pw_name) + 2);
- sprintf(matchline, "~%s", userdata->pw_name);
- matches[*num_matches] = matchline;
+ matches = (char **)nrealloc(matches, (*num_matches + 1) *
+ sizeof(char *));
+ matches[*num_matches] =
+ charalloc(strlen(userdata->pw_name) + 2);
+ sprintf(matches[*num_matches], "~%s", userdata->pw_name);
++(*num_matches);
-
- /* If there's no more room, bail out */
- if (*num_matches == BUFSIZ)
- break;
}
}
endpwent();
@@ -2047,365 +2065,271 @@ char **username_tab_completion(char *buf, int *num_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)
+ * worried about executables, only filenames :> */
+char **cwd_tab_completion(const char *buf, size_t *num_matches, size_t
+ buflen)
{
- char *dirname, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
- char **matches = (char **)NULL;
+ char *dirname = mallocstrcpy(NULL, buf);
+ char *filename;
+#ifndef DISABLE_OPERATINGDIR
+ size_t dirnamelen;
+#endif
+ size_t filenamelen;
+ char **matches = NULL;
DIR *dir;
- struct dirent *next;
+ const struct dirent *next;
- matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
-
- /* Stick a wildcard onto the buf, for later use */
- strcat(buf, "*");
-
- /* Okie, if there's a / in the buffer, strip out the directory part */
- if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
- dirname = charalloc(strlen(buf) + 1);
- tmp = buf + strlen(buf);
- while (*tmp != '/' && tmp != buf)
- tmp--;
-
- tmp++;
-
- strncpy(dirname, buf, tmp - buf + 1);
- dirname[tmp - buf] = '\0';
+ assert(dirname != NULL && num_matches != NULL && buflen >= 0);
+ *num_matches = 0;
+ null_at(&dirname, buflen);
+
+ /* Okie, if there's a / in the buffer, strip out the directory
+ * part. */
+ filename = strrchr(dirname, '/');
+ if (filename != NULL) {
+ char *tmpdirname = filename + 1;
+
+ filename = mallocstrcpy(NULL, tmpdirname);
+ *tmpdirname = '\0';
+ tmpdirname = dirname;
+ dirname = real_dir_from_tilde(dirname);
+ free(tmpdirname);
} else {
-
- if ((dirname = getcwd(NULL, PATH_MAX + 1)) == NULL)
- return matches;
- else
- tmp = buf;
+ filename = dirname;
+ dirname = mallocstrcpy(NULL, "./");
}
-#ifdef DEBUG
- fprintf(stderr, "\nDir = %s\n", dirname);
- fprintf(stderr, "\nbuf = %s\n", buf);
- fprintf(stderr, "\ntmp = %s\n", tmp);
-#endif
-
- dirtmp = real_dir_from_tilde(dirname);
- free(dirname);
- dirname = dirtmp;
-
-#ifdef DEBUG
- fprintf(stderr, "\nDir = %s\n", dirname);
- fprintf(stderr, "\nbuf = %s\n", buf);
- fprintf(stderr, "\ntmp = %s\n", tmp);
-#endif
-
+ assert(dirname[strlen(dirname) - 1] == '/');
dir = opendir(dirname);
+
if (dir == NULL) {
- /* Don't print an error, just shut up and return */
- *num_matches = 0;
+ /* Don't print an error, just shut up and return. */
beep();
- return matches;
+ free(filename);
+ free(dirname);
+ return NULL;
}
+
+#ifndef DISABLE_OPERATINGDIR
+ dirnamelen = strlen(dirname);
+#endif
+ filenamelen = strlen(filename);
+
while ((next = readdir(dir)) != NULL) {
#ifdef DEBUG
fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
#endif
- /* See if this matches */
- if (check_wildcard_match(next->d_name, tmp)) {
-
- /* Cool, found a match. Add it to the list
- * This makes a lot more sense to me (Chris) this way...
- */
+ /* See if this matches. */
+ if (strncmp(next->d_name, filename, filenamelen) == 0 &&
+ (*filename == '.' || (strcmp(next->d_name, ".") != 0 &&
+ strcmp(next->d_name, "..") != 0))) {
+ /* Cool, found a match. Add it to the list. This makes a
+ * lot more sense to me (Chris) this way... */
#ifndef DISABLE_OPERATINGDIR
/* ...unless the match exists outside the operating
- directory, in which case just go to the next match; to
- properly do operating directory checking, we have to add the
- directory name to the beginning of the proposed match
- before we check it */
-
- if (operating_dir != NULL) {
- tmp2 = charalloc(strlen(dirname) + strlen(next->d_name) + 2);
- strcpy(tmp2, dirname);
- strcat(tmp2, "/");
- strcat(tmp2, next->d_name);
- if (check_operating_dir(tmp2, TRUE) != 0) {
- free(tmp2);
- continue;
- }
- free(tmp2);
+ * directory, in which case just go to the next match. To
+ * properly do operating directory checking, we have to add
+ * the directory name to the beginning of the proposed match
+ * before we check it. */
+ char *tmp2 = charalloc(strlen(dirname) +
+ strlen(next->d_name) + 1);
+
+ sprintf(tmp2, "%s%s", dirname, next->d_name);
+ if (check_operating_dir(tmp2, TRUE)) {
+ free(tmp2);
+ continue;
}
+ free(tmp2);
#endif
- tmp2 = NULL;
- tmp2 = charalloc(strlen(next->d_name) + 1);
- strcpy(tmp2, next->d_name);
- matches[*num_matches] = tmp2;
- ++*num_matches;
-
- /* If there's no more room, bail out */
- if (*num_matches == BUFSIZ)
- break;
+ matches = (char **)nrealloc(matches, (*num_matches + 1) *
+ sizeof(char *));
+ matches[*num_matches] = mallocstrcpy(NULL, next->d_name);
+ ++(*num_matches);
}
}
closedir(dir);
free(dirname);
+ free(filename);
return matches;
}
-/* This function now has an arg which refers to how much the statusbar
- * (place) should be advanced, i.e. the new cursor pos. */
-char *input_tab(char *buf, int place, bool *lastwastab, int *newplace,
- bool *list)
+/* Do tab completion. This function now has an arg which refers to how
+ * much the statusbar cursor position (place) should be advanced. */
+char *input_tab(char *buf, size_t *place, bool *lastwastab, bool *list)
{
- /* Do TAB completion */
- static int num_matches = 0, match_matches = 0;
- static char **matches = (char **)NULL;
- int pos = place, i = 0, col = 0, editline = 0;
- int longestname = 0, is_dir = 0;
- char *foo;
-
- *list = FALSE;
-
- if (*lastwastab == FALSE) {
- char *tmp, *copyto, *matchbuf;
-
- *lastwastab = TRUE;
-
- /* Make a local copy of the string -- up to the position of the
- cursor */
- matchbuf = charalloc(strlen(buf) + 2);
- memset(matchbuf, '\0', strlen(buf) + 2);
-
- strncpy(matchbuf, buf, place);
- tmp = matchbuf;
-
- /* skip any leading white space */
- while (*tmp && is_blank_char(*tmp))
- ++tmp;
-
- /* Free up any memory already allocated */
- if (matches != NULL) {
- for (i = i; i < num_matches; i++)
- free(matches[i]);
- free(matches);
- matches = (char **)NULL;
- num_matches = 0;
- }
+ size_t num_matches = 0;
+ char **matches = NULL;
- /* If the word starts with `~' and there is no slash in the word,
- * then try completing this word as a username. */
-
- /* If the original string begins with a tilde, and the part
- we're trying to tab-complete doesn't contain a slash, copy
- the part we're tab-completing into buf, so tab completion
- will result in buf's containing only the tab-completed
- username. */
- if (buf[0] == '~' && strchr(tmp, '/') == NULL) {
- buf = mallocstrcpy(buf, tmp);
- matches = username_tab_completion(tmp, &num_matches);
- }
- /* If we're in the middle of the original line, copy the string
- only up to the cursor position into buf, so tab completion
- will result in buf's containing only the tab-completed
- path/filename. */
- else if (strlen(buf) > strlen(tmp))
- buf = mallocstrcpy(buf, tmp);
+ assert(buf != NULL && place != NULL && *place <= strlen(buf) && lastwastab != NULL && list != NULL);
- /* Try to match everything in the current working directory that
- * matches. */
- if (matches == NULL)
- matches = cwd_tab_completion(tmp, &num_matches);
+ *list = 0;
- /* Don't leak memory */
- free(matchbuf);
+ /* If the word starts with `~' and there is no slash in the word,
+ * then try completing this word as a username. */
+ if (*place > 0 && *buf == '~') {
+ const char *bob = strchr(buf, '/');
-#ifdef DEBUG
- fprintf(stderr, "%d matches found...\n", num_matches);
-#endif
- /* Did we find exactly one match? */
- switch (num_matches) {
- case 0:
- blank_edit();
- wrefresh(edit);
- break;
- case 1:
+ if (bob == NULL || bob >= buf + *place)
+ matches = username_tab_completion(buf, &num_matches,
+ *place);
+ }
- buf = charealloc(buf, strlen(buf) + strlen(matches[0]) + 1);
+ /* Match against files relative to the current working directory. */
+ if (matches == NULL)
+ matches = cwd_tab_completion(buf, &num_matches, *place);
- if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
- for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
- tmp--);
- tmp++;
- } else
- tmp = buf;
-
- if (strcmp(tmp, matches[0]) == 0)
- is_dir = append_slash_if_dir(buf, lastwastab, newplace);
+ if (num_matches <= 0)
+ beep();
+ else {
+ size_t match, common_len = 0;
+ size_t lastslash = strrchrn(buf, '/', *place);
+ /* Ignore the first match_strip characters of matches
+ * entries. The entries of matches are tilde expanded. */
+ char *mzero;
+
+ while (TRUE) {
+ for (match = 1; match < num_matches; match++) {
+ if (matches[0][common_len] !=
+ matches[match][common_len])
+ break;
+ }
- if (is_dir != 0)
+ if (match < num_matches || matches[0][common_len] == '\0')
break;
- copyto = tmp;
- for (pos = 0; *tmp == matches[0][pos] &&
- pos <= strlen(matches[0]); pos++)
- tmp++;
+ common_len++;
+ }
- /* write out the matched name */
- strncpy(copyto, matches[0], strlen(matches[0]) + 1);
- *newplace += strlen(matches[0]) - pos;
+ mzero = charalloc(lastslash + common_len + 1);
+ sprintf(mzero, "%.*s%.*s", lastslash, buf, common_len,
+ matches[0]);
- /* if an exact match is typed in and Tab is pressed,
- *newplace will now be negative; in that case, make it
- zero, so that the cursor will stay where it is instead of
- moving backward */
- if (*newplace < 0)
- *newplace = 0;
+ common_len += lastslash;
- /* Is it a directory? */
- append_slash_if_dir(buf, lastwastab, newplace);
+ assert(common_len >= *place);
- break;
- default:
- /* Check to see if all matches share a beginning, and, if so,
- tack it onto buf and then beep */
-
- if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
- for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
- tmp--);
- tmp++;
- } else
- tmp = buf;
+ if (num_matches == 1 && is_dir(mzero)) {
+ mzero[common_len] = '/';
+ common_len++;
+ assert(common_len > *place);
+ }
- for (pos = 0; *tmp == matches[0][pos] && *tmp != '\0' &&
- pos <= strlen(matches[0]); pos++)
- tmp++;
+ if (num_matches > 1 && (common_len != *place ||
+ *lastwastab == FALSE))
+ beep();
- while (TRUE) {
- match_matches = 0;
+ /* If there is more match to display on the statusbar, show it.
+ * We reset lastwastab to FALSE: it requires hitting Tab twice
+ * in succession with no statusbar changes to see a match
+ * list. */
+ if (common_len != *place) {
+ size_t buflen = strlen(buf);
+
+ *lastwastab = FALSE;
+ buf = charealloc(buf, common_len + buflen - *place + 1);
+ charmove(buf + common_len, buf + *place, buflen - *place + 1);
+ strncpy(buf, mzero, common_len);
+ *place = common_len;
+ } else if (*lastwastab == FALSE || num_matches < 2)
+ *lastwastab = TRUE;
+ else {
+ int longest_name = 0, editline = 0;
+ size_t columns;
- for (i = 0; i < num_matches; i++) {
- if (matches[i][pos] == 0)
- break;
- else if (matches[i][pos] == matches[0][pos])
- match_matches++;
- }
- if (match_matches == num_matches &&
- (i == num_matches || matches[i] != 0)) {
- /* All the matches have the same character at pos+1,
- so paste it into buf... */
- buf = charealloc(buf, strlen(buf) + 2);
- strncat(buf, matches[0] + pos, 1);
- *newplace += 1;
- pos++;
- } else {
- beep();
+ /* Now we show a list of the available choices. */
+ assert(num_matches > 1);
+
+ /* Sort the list. */
+ qsort(matches, num_matches, sizeof(char *), diralphasort);
+
+ for (match = 0; match < num_matches; match++) {
+ common_len = strnlenpt(matches[match], COLS - 1);
+ if (common_len > COLS - 1) {
+ longest_name = COLS - 1;
break;
}
+ if (common_len > longest_name)
+ longest_name = common_len;
}
- }
- } else {
- /* Ok -- the last char was a TAB. Since they
- * just hit TAB again, print a list of all the
- * available choices... */
- if (matches != NULL && num_matches > 1) {
-
- /* Blank the edit window, and print the matches out there */
- blank_edit();
- wmove(edit, 0, 0);
- editline = 0;
+ assert(longest_name <= COLS - 1);
- /* Figure out the length of the longest filename */
- for (i = 0; i < num_matches; i++)
- if (strlen(matches[i]) > longestname)
- longestname = strlen(matches[i]);
+ /* Each column will be longest_name + 2 characters wide,
+ * i.e, two spaces between columns, except that there will
+ * be only one space after the last column. */
+ columns = (COLS + 1) / (longest_name + 2);
- if (longestname > COLS - 1)
- longestname = COLS - 1;
+ /* Blank the edit window, and print the matches out
+ * there. */
+ blank_edit();
+ wmove(edit, 0, 0);
- foo = charalloc(longestname + 5);
+ /* Disable el cursor. */
+ curs_set(0);
- /* Print the list of matches */
- for (i = 0, col = 0; i < num_matches; i++) {
+ for (match = 0; match < num_matches; match++) {
+ char *disp;
- /* make each filename shown be the same length as the
- longest filename, with two spaces at the end */
- snprintf(foo, longestname + 1, "%s", matches[i]);
- while (strlen(foo) < longestname)
- strcat(foo, " ");
+ wmove(edit, editline, (longest_name + 2) *
+ (match % columns));
- strcat(foo, " ");
+ if (match % columns == 0 && editline == editwinrows - 1
+ && num_matches - match > columns) {
+ waddstr(edit, _("(more)"));
+ break;
+ }
- /* Disable el cursor */
- curs_set(0);
- /* now, put the match on the screen */
- waddnstr(edit, foo, strlen(foo));
- col += strlen(foo);
+ disp = display_string(matches[match], 0, longest_name,
+ FALSE);
+ waddstr(edit, disp);
+ free(disp);
- /* And if the next match isn't going to fit on the
- line, move to the next one */
- if (col > COLS - longestname && i + 1 < num_matches) {
+ if ((match + 1) % columns == 0)
editline++;
- wmove(edit, editline, 0);
- if (editline == editwinrows - 1) {
- waddstr(edit, _("(more)"));
- break;
- }
- col = 0;
- }
}
- free(foo);
wrefresh(edit);
*list = TRUE;
- } else
- beep();
+ }
+
+ free(mzero);
}
+ free_charptrarray(matches, num_matches);
+
/* Only refresh the edit window if we don't have a list of filename
- matches on it */
+ * matches on it. */
if (*list == FALSE)
edit_refresh();
+
+ /* Enable el cursor. */
curs_set(1);
+
return buf;
}
#endif /* !DISABLE_TABCOMP */
-/* Only print the last part of a path; isn't there a shell
- * command for this? */
+/* Only print the last part of a path. Isn't there a shell command for
+ * this? */
const char *tail(const char *foo)
{
- const char *tmp = foo + strlen(foo);
+ const char *tmp = strrchr(foo, '/');
- while (*tmp != '/' && tmp != foo)
- tmp--;
-
- if (*tmp == '/')
+ if (tmp == NULL)
+ tmp = foo;
+ else if (*tmp == '/')
tmp++;
return tmp;
}
#ifndef DISABLE_BROWSER
-/* Our sort routine for file listings -- sort directories before
- * files, and then alphabetically. */
-int diralphasort(const void *va, const void *vb)
-{
- struct stat fileinfo;
- const char *a = *(char *const *)va, *b = *(char *const *)vb;
- int aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
- int bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
-
- if (aisdir != 0 && bisdir == 0)
- return -1;
- if (aisdir == 0 && bisdir != 0)
- return 1;
-
- return strcasecmp(a, b);
-}
-
-/* Free our malloc()ed memory */
+/* Free our malloc()ed memory. */
void free_charptrarray(char **array, size_t len)
{
for (; len > 0; len--)
@@ -2419,25 +2343,10 @@ void striponedir(char *foo)
char *tmp;
assert(foo != NULL);
- /* Don't strip the root dir */
- if (*foo == '\0' || strcmp(foo, "/") == 0)
- return;
- tmp = foo + strlen(foo) - 1;
- assert(tmp >= foo);
- if (*tmp == '/')
- *tmp = '\0';
-
- while (*tmp != '/' && tmp != foo)
- tmp--;
-
- if (tmp != foo)
- *tmp = '\0';
- else { /* SPK may need to make a 'default' path here */
- if (*tmp != '/')
- *tmp = '.';
- *(tmp + 1) = '\0';
- }
+ tmp = strrchr(foo, '/');
+ if (tmp != NULL)
+ *tmp = '\0';
}
int readable_dir(const char *path)
@@ -2528,7 +2437,7 @@ char *do_browser(const char *inpath)
filelist = browser_init(path, &longest, &numents);
foo = charalloc(longest + 8);
- /* Sort the list by directory first, then alphabetically */
+ /* Sort the list. */
qsort(filelist, numents, sizeof(char *), diralphasort);
titlebar(path);
diff --git a/src/global.c b/src/global.c
@@ -183,9 +183,7 @@ bool curses_ended = FALSE; /* Indicates to statusbar() to simply
* write to stderr, since endwin() has
* ended curses mode. */
-#ifdef ENABLE_NANORC
char *homedir = NULL; /* $HOME or from /etc/passwd. */
-#endif
size_t length_of_list(const shortcut *s)
{
diff --git a/src/proto.h b/src/proto.h
@@ -144,25 +144,23 @@ extern historyheadtype replace_history;
extern bool curses_ended;
-#ifdef ENABLE_NANORC
extern char *homedir;
-#endif
/* Functions we want available. */
/* Public functions in chars.c. */
-bool is_byte(unsigned int c);
-bool is_alnum_char(unsigned int c);
+bool is_byte(int c);
+bool is_alnum_char(int c);
bool is_alnum_mbchar(const char *c);
#ifdef NANO_WIDE
bool is_alnum_wchar(wchar_t wc);
#endif
-bool is_blank_char(unsigned int c);
+bool is_blank_char(int c);
bool is_blank_mbchar(const char *c);
#ifdef NANO_WIDE
bool is_blank_wchar(wchar_t wc);
#endif
-bool is_cntrl_char(unsigned int c);
+bool is_cntrl_char(int c);
bool is_cntrl_mbchar(const char *c);
#ifdef NANO_WIDE
bool is_cntrl_wchar(wchar_t wc);
@@ -174,7 +172,7 @@ wchar_t control_wrep(wchar_t c);
#endif
int mbwidth(const char *c);
int mb_cur_max(void);
-char *make_mbchar(unsigned int chr, char *chr_mb, int *chr_mb_len);
+char *make_mbchar(int chr, char *chr_mb, int *chr_mb_len);
int parse_mbchar(const char *buf, char *chr
#ifdef NANO_WIDE
, bool *bad_chr
@@ -207,6 +205,7 @@ size_t mbstrlen(const char *s);
size_t nstrnlen(const char *s, size_t maxlen);
#endif
size_t mbstrnlen(const char *s, size_t maxlen);
+size_t strrchrn(const char *s, int c, size_t n);
/* Public functions in color.c. */
#ifdef ENABLE_COLOR
@@ -287,16 +286,18 @@ int write_marked(const char *name, bool tmp, int append);
int do_writeout(bool exiting);
void do_writeout_void(void);
char *real_dir_from_tilde(const char *buf);
+#if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER)
+int diralphasort(const void *va, const void *vb);
+#endif
#ifndef DISABLE_TABCOMP
-int append_slash_if_dir(char *buf, bool *lastwastab, int *place);
-char **username_tab_completion(char *buf, int *num_matches);
-char **cwd_tab_completion(char *buf, int *num_matches);
-char *input_tab(char *buf, int place, bool *lastwastab, int *newplace,
- bool *list);
+char **username_tab_completion(const char *buf, size_t *num_matches,
+ size_t buflen);
+char **cwd_tab_completion(const char *buf, size_t *num_matches, size_t
+ buflen);
+char *input_tab(char *buf, size_t *place, bool *lastwastab, bool *list);
#endif
const char *tail(const char *foo);
#ifndef DISABLE_BROWSER
-int diralphasort(const void *va, const void *vb);
void free_charptrarray(char **array, size_t len);
void striponedir(char *foo);
int readable_dir(const char *path);
@@ -531,6 +532,7 @@ int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
int regexp_bol_or_eol(const regex_t *preg, const char *string);
#endif
int num_of_digits(int n);
+void get_homedir(void);
bool parse_num(const char *str, ssize_t *val);
void align(char **strp);
void null_at(char **data, size_t index);
@@ -560,9 +562,6 @@ void mark_order(const filestruct **top, size_t *top_x, const filestruct
#endif
void get_totals(const filestruct *begin, const filestruct *end, int
*lines, size_t *size);
-#ifndef DISABLE_TABCOMP
-int check_wildcard_match(const char *text, const char *pattern);
-#endif
/* Public functions in winio.c. */
#ifndef NANO_SMALL
diff --git a/src/rcfile.c b/src/rcfile.c
@@ -30,7 +30,6 @@
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
-#include <pwd.h>
#include <ctype.h>
#include <assert.h>
#include "proto.h"
@@ -666,18 +665,7 @@ void do_rcfile(void)
lineno = 0;
- {
- const char *homenv = getenv("HOME");
-
- /* Rely on $HOME, fall back on getpwuid() */
- if (homenv == NULL) {
- const struct passwd *userage = getpwuid(geteuid());
-
- if (userage != NULL)
- homenv = userage->pw_dir;
- }
- homedir = mallocstrcpy(NULL, homenv);
- }
+ get_homedir();
if (homedir == NULL) {
rcfile_error(N_("I can't find my home directory! Wah!"));
diff --git a/src/utils.c b/src/utils.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
+#include <pwd.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
@@ -67,6 +68,23 @@ int num_of_digits(int n)
return i;
}
+/* Return the user's home directory. We use $HOME, and if that fails,
+ * we fall back on getpwuid(). */
+void get_homedir(void)
+{
+ if (homedir == NULL) {
+ const char *homenv = getenv("HOME");
+
+ if (homenv == NULL) {
+ const struct passwd *userage = getpwuid(geteuid());
+
+ if (userage != NULL)
+ homenv = userage->pw_dir;
+ }
+ homedir = mallocstrcpy(NULL, homenv);
+ }
+}
+
/* Read a ssize_t from str, and store it in *val (if val is not NULL).
* On error, we return FALSE and don't change *val. Otherwise, we
* return TRUE. */
@@ -414,105 +432,3 @@ void get_totals(const filestruct *begin, const filestruct *end, int
}
}
}
-
-#ifndef DISABLE_TABCOMP
-/*
- * 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 != '\0' || *pattern != '\0') {
- 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 && len != 0) {
- return FALSE;
- }
- if (found) {
- 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 != '\0')
- text++;
- break;
- }
-
- if (*text != '\0') {
- pattern = retrypat;
- text = ++retrytext;
- break;
- }
-
- return FALSE;
- }
-
- if (pattern == NULL)
- return FALSE;
- }
-
- return TRUE;
-}
-#endif
diff --git a/src/winio.c b/src/winio.c
@@ -2530,17 +2530,11 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *def,
#endif
#ifndef DISABLE_TABCOMP
if (allow_tabs) {
- int shift = 0;
-
- answer = input_tab(answer, statusbar_x, &tabbed, &shift,
- list);
- statusbar_xend = strlen(answer);
- statusbar_x += shift;
- if (statusbar_x > statusbar_xend)
- statusbar_x = statusbar_xend;
+ answer = input_tab(answer, &statusbar_x, &tabbed, list);
+ statusbar_xend = statusbar_x;
}
-#endif
break;
+#endif
case NANO_PREVLINE_KEY:
#ifndef NANO_SMALL
if (history_list != NULL) {