Implemented username tab completion, cleaned up existing tabcomp code, added --disable-tabcomp option
git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@313 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
diff --git a/files.c b/files.c
index 1fb4e29..cad218b 100644
--- a/files.c
+++ b/files.c
@@ -295,6 +295,7 @@
filestruct *fileptr;
int fd, mask = 0;
struct stat st;
+ char *realname = NULL;
if (!strcmp(name, "")) {
statusbar(_("Cancelled"));
@@ -302,17 +303,21 @@
}
titlebar();
fileptr = fileage;
-
+#ifndef DISABLE_TABCOMP
+ realname = real_dir_from_tilde(name);
+#else
+ realname = mallocstrcpy(realname, name);
+#endif
/* Check to see if the file is a regular file and FOLLOW_SYMLINKS is
set. If so then don't do the delete and recreate code which would
cause unexpected behavior */
- lstat(name, &st);
+ lstat(realname, &st);
/* Open the file and truncate it. Trust the symlink. */
if ((ISSET(FOLLOW_SYMLINKS) || !S_ISLNK(st.st_mode)) && !tmp) {
- if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,
+ if ((fd = open(realname, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
S_IWOTH)) == -1) {
if (ISSET(TEMP_OPT)) {
@@ -321,18 +326,19 @@
}
statusbar(_("Could not open file for writing: %s"),
strerror(errno));
+ free(realname);
return -1;
}
}
/* Don't follow symlink. Create new file. */
else {
- if (strlen(name) > (PATH_MAX - 7)) {
+ if (strlen(realname) > (PATH_MAX - 7)) {
statusbar(_("Could not open file: Path length exceeded."));
return -1;
}
memset(buf, 0x00, PATH_MAX + 1);
- strcat(buf, name);
+ strcat(buf, realname);
strcat(buf, ".XXXXXX");
if ((fd = mkstemp(buf)) == -1) {
if (ISSET(TEMP_OPT)) {
@@ -385,13 +391,13 @@
if (close(fd) == -1) {
- statusbar(_("Could not close %s: %s"), name, strerror(errno));
+ statusbar(_("Could not close %s: %s"), realname, strerror(errno));
unlink(buf);
return -1;
}
if (!ISSET(FOLLOW_SYMLINKS) || tmp) {
- if (stat(name, &st) == -1) {
+ if (stat(realname, &st) == -1) {
/* Use default umask as file permisions if file is a new file. */
mask = umask(0);
umask(mask);
@@ -404,37 +410,37 @@
} else {
/* Use permissions from file we are overwriting. */
mask = st.st_mode;
- if (unlink(name) == -1) {
+ if (unlink(realname) == -1) {
if (errno != ENOENT) {
statusbar(_("Could not open %s for writing: %s"),
- name, strerror(errno));
+ realname, strerror(errno));
unlink(buf);
return -1;
}
}
}
- if (link(buf, name) != -1)
+ if (link(buf, realname) != -1)
unlink(buf);
else if (errno != EPERM) {
statusbar(_("Could not open %s for writing: %s"),
name, strerror(errno));
unlink(buf);
return -1;
- } else if (rename(buf, name) == -1) { /* Try a rename?? */
- statusbar(_("Could not open %s for writing: %s"),
- name, strerror(errno));
+ } else if (rename(buf, realname) == -1) { /* Try a rename?? */
+ statusbar(_("Could not open %s for writing: %s"),
+ realname, strerror(errno));
unlink(buf);
return -1;
}
- if (chmod(name, mask) == -1) {
+ if (chmod(realname, mask) == -1) {
statusbar(_("Could not set permissions %o on %s: %s"),
- mask, name, strerror(errno));
+ mask, realname, strerror(errno));
}
}
if (!tmp) {
- strncpy(filename, name, 132);
+ strncpy(filename, realname, 132);
statusbar(_("Wrote %d lines"), lineswritten);
}
UNSET(MODIFIED);
@@ -497,6 +503,58 @@
return do_writeout(0);
}
+#ifndef DISABLE_TABCOMP
+static char **homedirs;
+
+/* Return a malloc()ed string containing the actual directory, used
+ * to convert ~user and ~/ notation...
+ */
+char *real_dir_from_tilde (char *buf)
+{
+ char *dirtmp = NULL, *tmp;
+
+ if (buf[0] == '~') {
+
+ if (buf[1] == '/') {
+ if (getenv("HOME") != NULL) {
+ dirtmp = nmalloc(strlen(buf) + 2 + strlen(getenv("HOME")));
+
+ sprintf(dirtmp, "%s/%s", getenv("HOME"), &buf[2]);
+ }
+ }
+ else if (buf[1] != 0) {
+ dirtmp = nmalloc(strlen(buf) + 2 + strlen(homedirs[0]));
+ for (tmp = &buf[2]; *tmp != '/' && *tmp != 0; tmp++);
+
+ sprintf(dirtmp, "%s%s", homedirs[0], tmp);
+ }
+ }
+ else
+ dirtmp = mallocstrcpy(dirtmp, buf);
+
+ return dirtmp;
+}
+
+/* Tack a slash onto the string we're completing if it's a directory */
+void append_slash_if_dir(char *buf, int *lastWasTab, int *place)
+{
+ char *dirptr;
+ struct stat fileinfo;
+
+ dirptr = real_dir_from_tilde(buf);
+
+ if (stat(dirptr, &fileinfo) == -1)
+ ;
+ else if (S_ISDIR(fileinfo.st_mode)) {
+ strncat(buf, "/", 1);
+ *place += 1;
+ /* now we start over again with # of tabs so far */
+ *lastWasTab = 0;
+ }
+
+ if (dirptr != buf)
+ free(dirptr);
+}
/*
* These functions (username_tab_completion, cwd_tab_completion, and
@@ -517,24 +575,90 @@
* This code may safely be consumed by a BSD or GPL license.
*/
-#if 0
char **username_tab_completion(char *buf, int *num_matches)
{
- char **matches = (char **) NULL;
- *num_matches = 0;
+ char **matches = (char **) NULL, *line = NULL, *lineptr;
+ char *matchline = NULL, *matchdir = NULL;
+
+ int fd, i = 0, status = 1;
+ char byte[1];
+
+ if((fd = open("/etc/passwd", O_RDONLY)) == -1) {
+ return NULL;
+ }
+
+ if (homedirs != NULL) {
+ for (i = 0; i < *num_matches; i++)
+ free(homedirs[i]);
+ free(homedirs);
+ homedirs = (char **) NULL;
+ *num_matches = 0;
+ }
+ matches = nmalloc(BUFSIZ);
+ homedirs = nmalloc(BUFSIZ);
+ strcat(buf, "*");
+ do {
+ i = 0;
+ line = nmalloc(1);
+ while ((status = read(fd, byte, 1)) != 0 && byte[0] != '\n') {
+
+ line[i] = byte[0];
+ i++;
+ line = nrealloc(line, i+1);
+ }
+
+ if (i == 0)
+ break;
+
+ line[i] = 0;
+ lineptr = strtok(line, ":");
+
+ if (check_wildcard_match(line, &buf[1]) == TRUE) {
+
+ if (*num_matches == BUFSIZ)
+ break;
+
+ /* Cool, found a match. Add it to the list
+ * This makes a lot more sense to me (Chris) this way...
+ */
+ matchline = nmalloc(strlen(line) + 2);
+ sprintf(matchline, "~%s", line);
+
+ for (i = 0; i <= 4 && lineptr != NULL; i++)
+ lineptr = strtok(NULL, ":");
+
+ if (lineptr == NULL)
+ break;
+
+ matchdir = mallocstrcpy(matchdir, lineptr);
+ homedirs[*num_matches] = matchdir;
+ matches[*num_matches] = matchline;
+
+ ++*num_matches;
+
+ /* If there's no more room, bail out */
+ if (*num_matches == BUFSIZ)
+ break;
+ }
+
+ free(line);
+
+ } while (status != 0);
+
+ close(fd);
+ return matches;
#ifdef DEBUG
fprintf(stderr, "\nin username_tab_completion\n");
#endif
return (matches);
}
-#endif
/* 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, *tmp2 = NULL;
+ char *dirName, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
char **matches = (char **) NULL;
DIR *dir;
struct dirent *next;
@@ -569,6 +693,17 @@
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
+
+
dir = opendir(dirName);
if (!dir) {
/* Don't print an error, just shut up and return */
@@ -618,7 +753,6 @@
int pos = place, i = 0, col = 0, editline = 0;
int longestname = 0;
char *foo;
- struct stat fileinfo;
if (*lastWasTab == FALSE) {
char *tmp, *copyto, *matchBuf;
@@ -648,9 +782,9 @@
/* 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!
+ /* FIXME -- this check is broken! */
if (*tmp == '~' && !strchr(tmp, '/'))
- matches = username_tab_completion(tmp, &num_matches); */
+ matches = username_tab_completion(tmp, &num_matches);
/* Try to match everything in the current working directory that
* matches. */
@@ -680,21 +814,8 @@
} else
tmp = buf;
- if (!strcmp(tmp, matches[0])) {
-
- /* Is it a directory? */
- if (stat(buf, &fileinfo) == -1)
- break;
- else if (S_ISDIR(fileinfo.st_mode)) {
-
- /* Tack on a slash */
- strncat(buf, "/", 1);
- *newplace += 1;
- /* now we start over with 0 tabs so far */
- *lastWasTab = 0;
- }
- break;
- }
+ if (!strcmp(tmp, matches[0]))
+ append_slash_if_dir(buf, lastWasTab, newplace);
copyto = tmp;
for (pos = 0; *tmp == matches[0][pos] &&
@@ -705,14 +826,9 @@
strncpy(copyto, matches[0], strlen(matches[0]) + 1);
*newplace += strlen(matches[0]) - pos;
- if (stat(buf, &fileinfo) == -1)
- break;
- else if (S_ISDIR(fileinfo.st_mode)) {
- strncat(buf, "/", 1);
- *newplace += 1;
- /* now we start over again with # of tabs so far */
- *lastWasTab = 0;
- }
+ /* Is it a directory? */
+ append_slash_if_dir(buf, lastWasTab, newplace);
+
break;
default:
/* Check to see if all matches share a beginning, and if so
@@ -815,3 +931,4 @@
curs_set(1);
return buf;
}
+#endif