From 65e6ecb1d47a6167649a647e813cc0302673875c Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Tue, 8 Feb 2005 20:37:53 +0000 Subject: [PATCH] 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 --- ChangeLog | 9 + src/chars.c | 31 ++- src/files.c | 615 ++++++++++++++++++++++----------------------------- src/global.c | 2 - src/proto.h | 31 ++- src/rcfile.c | 14 +- src/utils.c | 120 ++-------- src/winio.c | 12 +- 8 files changed, 331 insertions(+), 503 deletions(-) diff --git a/ChangeLog b/ChangeLog index 986b3897..f827a956 100644 --- 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 index 356ecc50..dc7303ce 100644 --- 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 index 1a50b483..73446e79 100644 --- 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 index b714c744..7d5ce48f 100644 --- 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 index 145e381b..26940c1b 100644 --- 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 index 649e72e4..16999c20 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #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 index b0a33019..7739cec1 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -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 index 225c0299..fe54fa1e 100644 --- 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) { -- 2.39.5