summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
authorDarren Tucker <dtucker@zip.com.au>2010-01-08 19:02:40 +1100
committerDarren Tucker <dtucker@zip.com.au>2010-01-08 19:02:40 +1100
commit909d858d6b86c789003b809a636c2c228d3f30c5 (patch)
tree16e97da15727ad115afc97bba02f636f81d37915 /sftp.c
parent0c348f5b9eb4ac9f763cb3f55a252d86e2cfc57b (diff)
- djm@cvs.openbsd.org 2010/01/04 02:03:57
[sftp.c] Implement tab-completion of commands, local and remote filenames for sftp. Hacked on and off for some time by myself, mouring, Carlos Silva (via 2009 Google Summer of Code) and polished to a fine sheen by myself again. It should deal more-or-less correctly with the ikky corner-cases presented by quoted filenames, but the UI could still be slightly improved. In particular, it is quite slow for remote completion on large directories. bz#200; ok markus@
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c484
1 files changed, 434 insertions, 50 deletions
diff --git a/sftp.c b/sftp.c
index d8728cc25..6a5ccc49d 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.115 2009/12/20 07:28:36 guenther Exp $ */ 1/* $OpenBSD: sftp.c,v 1.116 2010/01/04 02:03:57 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -95,6 +95,12 @@ volatile sig_atomic_t interrupted = 0;
95/* I wish qsort() took a separate ctx for the comparison function...*/ 95/* I wish qsort() took a separate ctx for the comparison function...*/
96int sort_flag; 96int sort_flag;
97 97
98/* Context used for commandline completion */
99struct complete_ctx {
100 struct sftp_conn *conn;
101 char **remote_pathp;
102};
103
98int remote_glob(struct sftp_conn *, const char *, int, 104int remote_glob(struct sftp_conn *, const char *, int,
99 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 105 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
100 106
@@ -145,43 +151,47 @@ extern char *__progname;
145struct CMD { 151struct CMD {
146 const char *c; 152 const char *c;
147 const int n; 153 const int n;
154 const int t;
148}; 155};
149 156
157/* Type of completion */
158#define NOARGS 0
159#define REMOTE 1
160#define LOCAL 2
161
150static const struct CMD cmds[] = { 162static const struct CMD cmds[] = {
151 { "bye", I_QUIT }, 163 { "bye", I_QUIT, NOARGS },
152 { "cd", I_CHDIR }, 164 { "cd", I_CHDIR, REMOTE },
153 { "chdir", I_CHDIR }, 165 { "chdir", I_CHDIR, REMOTE },
154 { "chgrp", I_CHGRP }, 166 { "chgrp", I_CHGRP, REMOTE },
155 { "chmod", I_CHMOD }, 167 { "chmod", I_CHMOD, REMOTE },
156 { "chown", I_CHOWN }, 168 { "chown", I_CHOWN, REMOTE },
157 { "df", I_DF }, 169 { "df", I_DF, REMOTE },
158 { "dir", I_LS }, 170 { "dir", I_LS, REMOTE },
159 { "exit", I_QUIT }, 171 { "exit", I_QUIT, NOARGS },
160 { "get", I_GET }, 172 { "get", I_GET, REMOTE },
161 { "mget", I_GET }, 173 { "help", I_HELP, NOARGS },
162 { "help", I_HELP }, 174 { "lcd", I_LCHDIR, LOCAL },
163 { "lcd", I_LCHDIR }, 175 { "lchdir", I_LCHDIR, LOCAL },
164 { "lchdir", I_LCHDIR }, 176 { "lls", I_LLS, LOCAL },
165 { "lls", I_LLS }, 177 { "lmkdir", I_LMKDIR, LOCAL },
166 { "lmkdir", I_LMKDIR }, 178 { "ln", I_SYMLINK, REMOTE },
167 { "ln", I_SYMLINK }, 179 { "lpwd", I_LPWD, LOCAL },
168 { "lpwd", I_LPWD }, 180 { "ls", I_LS, REMOTE },
169 { "ls", I_LS }, 181 { "lumask", I_LUMASK, NOARGS },
170 { "lumask", I_LUMASK }, 182 { "mkdir", I_MKDIR, REMOTE },
171 { "mkdir", I_MKDIR }, 183 { "progress", I_PROGRESS, NOARGS },
172 { "progress", I_PROGRESS }, 184 { "put", I_PUT, LOCAL },
173 { "put", I_PUT }, 185 { "pwd", I_PWD, REMOTE },
174 { "mput", I_PUT }, 186 { "quit", I_QUIT, NOARGS },
175 { "pwd", I_PWD }, 187 { "rename", I_RENAME, REMOTE },
176 { "quit", I_QUIT }, 188 { "rm", I_RM, REMOTE },
177 { "rename", I_RENAME }, 189 { "rmdir", I_RMDIR, REMOTE },
178 { "rm", I_RM }, 190 { "symlink", I_SYMLINK, REMOTE },
179 { "rmdir", I_RMDIR }, 191 { "version", I_VERSION, NOARGS },
180 { "symlink", I_SYMLINK }, 192 { "!", I_SHELL, NOARGS },
181 { "version", I_VERSION }, 193 { "?", I_HELP, NOARGS },
182 { "!", I_SHELL }, 194 { NULL, -1, -1 }
183 { "?", I_HELP },
184 { NULL, -1}
185}; 195};
186 196
187int interactive_loop(struct sftp_conn *, char *file1, char *file2); 197int interactive_loop(struct sftp_conn *, char *file1, char *file2);
@@ -932,12 +942,23 @@ undo_glob_escape(char *s)
932 * Split a string into an argument vector using sh(1)-style quoting, 942 * Split a string into an argument vector using sh(1)-style quoting,
933 * comment and escaping rules, but with some tweaks to handle glob(3) 943 * comment and escaping rules, but with some tweaks to handle glob(3)
934 * wildcards. 944 * wildcards.
945 * The "sloppy" flag allows for recovery from missing terminating quote, for
946 * use in parsing incomplete commandlines during tab autocompletion.
947 *
935 * Returns NULL on error or a NULL-terminated array of arguments. 948 * Returns NULL on error or a NULL-terminated array of arguments.
949 *
950 * If "lastquote" is not NULL, the quoting character used for the last
951 * argument is placed in *lastquote ("\0", "'" or "\"").
952 *
953 * If "terminated" is not NULL, *terminated will be set to 1 when the
954 * last argument's quote has been properly terminated or 0 otherwise.
955 * This parameter is only of use if "sloppy" is set.
936 */ 956 */
937#define MAXARGS 128 957#define MAXARGS 128
938#define MAXARGLEN 8192 958#define MAXARGLEN 8192
939static char ** 959static char **
940makeargv(const char *arg, int *argcp) 960makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
961 u_int *terminated)
941{ 962{
942 int argc, quot; 963 int argc, quot;
943 size_t i, j; 964 size_t i, j;
@@ -951,6 +972,10 @@ makeargv(const char *arg, int *argcp)
951 error("string too long"); 972 error("string too long");
952 return NULL; 973 return NULL;
953 } 974 }
975 if (terminated != NULL)
976 *terminated = 1;
977 if (lastquote != NULL)
978 *lastquote = '\0';
954 state = MA_START; 979 state = MA_START;
955 i = j = 0; 980 i = j = 0;
956 for (;;) { 981 for (;;) {
@@ -967,6 +992,8 @@ makeargv(const char *arg, int *argcp)
967 if (state == MA_START) { 992 if (state == MA_START) {
968 argv[argc] = argvs + j; 993 argv[argc] = argvs + j;
969 state = q; 994 state = q;
995 if (lastquote != NULL)
996 *lastquote = arg[i];
970 } else if (state == MA_UNQUOTED) 997 } else if (state == MA_UNQUOTED)
971 state = q; 998 state = q;
972 else if (state == q) 999 else if (state == q)
@@ -1003,6 +1030,8 @@ makeargv(const char *arg, int *argcp)
1003 if (state == MA_START) { 1030 if (state == MA_START) {
1004 argv[argc] = argvs + j; 1031 argv[argc] = argvs + j;
1005 state = MA_UNQUOTED; 1032 state = MA_UNQUOTED;
1033 if (lastquote != NULL)
1034 *lastquote = '\0';
1006 } 1035 }
1007 if (arg[i + 1] == '?' || arg[i + 1] == '[' || 1036 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1008 arg[i + 1] == '*' || arg[i + 1] == '\\') { 1037 arg[i + 1] == '*' || arg[i + 1] == '\\') {
@@ -1028,6 +1057,12 @@ makeargv(const char *arg, int *argcp)
1028 goto string_done; 1057 goto string_done;
1029 } else if (arg[i] == '\0') { 1058 } else if (arg[i] == '\0') {
1030 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1059 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1060 if (sloppy) {
1061 state = MA_UNQUOTED;
1062 if (terminated != NULL)
1063 *terminated = 0;
1064 goto string_done;
1065 }
1031 error("Unterminated quoted argument"); 1066 error("Unterminated quoted argument");
1032 return NULL; 1067 return NULL;
1033 } 1068 }
@@ -1041,6 +1076,8 @@ makeargv(const char *arg, int *argcp)
1041 if (state == MA_START) { 1076 if (state == MA_START) {
1042 argv[argc] = argvs + j; 1077 argv[argc] = argvs + j;
1043 state = MA_UNQUOTED; 1078 state = MA_UNQUOTED;
1079 if (lastquote != NULL)
1080 *lastquote = '\0';
1044 } 1081 }
1045 if ((state == MA_SQUOTE || state == MA_DQUOTE) && 1082 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1046 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { 1083 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
@@ -1063,8 +1100,8 @@ makeargv(const char *arg, int *argcp)
1063} 1100}
1064 1101
1065static int 1102static int
1066parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag, 1103parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1067 unsigned long *n_arg, char **path1, char **path2) 1104 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1068{ 1105{
1069 const char *cmd, *cp = *cpp; 1106 const char *cmd, *cp = *cpp;
1070 char *cp2, **argv; 1107 char *cp2, **argv;
@@ -1086,7 +1123,7 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int
1086 cp++; 1123 cp++;
1087 } 1124 }
1088 1125
1089 if ((argv = makeargv(cp, &argc)) == NULL) 1126 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1090 return -1; 1127 return -1;
1091 1128
1092 /* Figure out which command we have */ 1129 /* Figure out which command we have */
@@ -1468,10 +1505,344 @@ prompt(EditLine *el)
1468} 1505}
1469#endif 1506#endif
1470 1507
1508/* Display entries in 'list' after skipping the first 'len' chars */
1509static void
1510complete_display(char **list, u_int len)
1511{
1512 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1513 struct winsize ws;
1514 char *tmp;
1515
1516 /* Count entries for sort and find longest */
1517 for (y = 0; list[y]; y++)
1518 m = MAX(m, strlen(list[y]));
1519
1520 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1521 width = ws.ws_col;
1522
1523 m = m > len ? m - len : 0;
1524 columns = width / (m + 2);
1525 columns = MAX(columns, 1);
1526 colspace = width / columns;
1527 colspace = MIN(colspace, width);
1528
1529 printf("\n");
1530 m = 1;
1531 for (y = 0; list[y]; y++) {
1532 llen = strlen(list[y]);
1533 tmp = llen > len ? list[y] + len : "";
1534 printf("%-*s", colspace, tmp);
1535 if (m >= columns) {
1536 printf("\n");
1537 m = 1;
1538 } else
1539 m++;
1540 }
1541 printf("\n");
1542}
1543
1544/*
1545 * Given a "list" of words that begin with a common prefix of "word",
1546 * attempt to find an autocompletion to extends "word" by the next
1547 * characters common to all entries in "list".
1548 */
1549static char *
1550complete_ambiguous(const char *word, char **list, size_t count)
1551{
1552 if (word == NULL)
1553 return NULL;
1554
1555 if (count > 0) {
1556 u_int y, matchlen = strlen(list[0]);
1557
1558 /* Find length of common stem */
1559 for (y = 1; list[y]; y++) {
1560 u_int x;
1561
1562 for (x = 0; x < matchlen; x++)
1563 if (list[0][x] != list[y][x])
1564 break;
1565
1566 matchlen = x;
1567 }
1568
1569 if (matchlen > strlen(word)) {
1570 char *tmp = xstrdup(list[0]);
1571
1572 tmp[matchlen] = NULL;
1573 return tmp;
1574 }
1575 }
1576
1577 return xstrdup(word);
1578}
1579
1580/* Autocomplete a sftp command */
1581static int
1582complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1583 int terminated)
1584{
1585 u_int y, count = 0, cmdlen, tmplen;
1586 char *tmp, **list, argterm[3];
1587 const LineInfo *lf;
1588
1589 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1590
1591 /* No command specified: display all available commands */
1592 if (cmd == NULL) {
1593 for (y = 0; cmds[y].c; y++)
1594 list[count++] = xstrdup(cmds[y].c);
1595
1596 list[count] = NULL;
1597 complete_display(list, 0);
1598
1599 for (y = 0; list[y] != NULL; y++)
1600 xfree(list[y]);
1601 xfree(list);
1602 return count;
1603 }
1604
1605 /* Prepare subset of commands that start with "cmd" */
1606 cmdlen = strlen(cmd);
1607 for (y = 0; cmds[y].c; y++) {
1608 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1609 list[count++] = xstrdup(cmds[y].c);
1610 }
1611 list[count] = NULL;
1612
1613 if (count == 0)
1614 return 0;
1615
1616 /* Complete ambigious command */
1617 tmp = complete_ambiguous(cmd, list, count);
1618 if (count > 1)
1619 complete_display(list, 0);
1620
1621 for (y = 0; list[y]; y++)
1622 xfree(list[y]);
1623 xfree(list);
1624
1625 if (tmp != NULL) {
1626 tmplen = strlen(tmp);
1627 cmdlen = strlen(cmd);
1628 /* If cmd may be extended then do so */
1629 if (tmplen > cmdlen)
1630 if (el_insertstr(el, tmp + cmdlen) == -1)
1631 fatal("el_insertstr failed.");
1632 lf = el_line(el);
1633 /* Terminate argument cleanly */
1634 if (count == 1) {
1635 y = 0;
1636 if (!terminated)
1637 argterm[y++] = quote;
1638 if (lastarg || *(lf->cursor) != ' ')
1639 argterm[y++] = ' ';
1640 argterm[y] = '\0';
1641 if (y > 0 && el_insertstr(el, argterm) == -1)
1642 fatal("el_insertstr failed.");
1643 }
1644 xfree(tmp);
1645 }
1646
1647 return count;
1648}
1649
1650/*
1651 * Determine whether a particular sftp command's arguments (if any)
1652 * represent local or remote files.
1653 */
1654static int
1655complete_is_remote(char *cmd) {
1656 int i;
1657
1658 if (cmd == NULL)
1659 return -1;
1660
1661 for (i = 0; cmds[i].c; i++) {
1662 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1663 return cmds[i].t;
1664 }
1665
1666 return -1;
1667}
1668
1669/* Autocomplete a filename "file" */
1670static int
1671complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1672 char *file, int remote, int lastarg, char quote, int terminated)
1673{
1674 glob_t g;
1675 char *tmp, *tmp2, ins[3];
1676 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1677 const LineInfo *lf;
1678
1679 /* Glob from "file" location */
1680 if (file == NULL)
1681 tmp = xstrdup("*");
1682 else
1683 xasprintf(&tmp, "%s*", file);
1684
1685 memset(&g, 0, sizeof(g));
1686 if (remote != LOCAL) {
1687 tmp = make_absolute(tmp, remote_path);
1688 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1689 } else
1690 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1691
1692 /* Determine length of pwd so we can trim completion display */
1693 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1694 /* Terminate counting on first unescaped glob metacharacter */
1695 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1696 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1697 hadglob = 1;
1698 break;
1699 }
1700 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1701 tmplen++;
1702 if (tmp[tmplen] == '/')
1703 pwdlen = tmplen + 1; /* track last seen '/' */
1704 }
1705 xfree(tmp);
1706
1707 if (g.gl_matchc == 0)
1708 goto out;
1709
1710 if (g.gl_matchc > 1)
1711 complete_display(g.gl_pathv, pwdlen);
1712
1713 tmp = NULL;
1714 /* Don't try to extend globs */
1715 if (file == NULL || hadglob)
1716 goto out;
1717
1718 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1719 tmp = path_strip(tmp2, remote_path);
1720 xfree(tmp2);
1721
1722 if (tmp == NULL)
1723 goto out;
1724
1725 tmplen = strlen(tmp);
1726 filelen = strlen(file);
1727
1728 if (tmplen > filelen) {
1729 tmp2 = tmp + filelen;
1730 len = strlen(tmp2);
1731 /* quote argument on way out */
1732 for (i = 0; i < len; i++) {
1733 ins[0] = '\\';
1734 ins[1] = tmp2[i];
1735 ins[2] = '\0';
1736 switch (tmp2[i]) {
1737 case '\'':
1738 case '"':
1739 case '\\':
1740 case '\t':
1741 case ' ':
1742 if (quote == '\0' || tmp2[i] == quote) {
1743 if (el_insertstr(el, ins) == -1)
1744 fatal("el_insertstr "
1745 "failed.");
1746 break;
1747 }
1748 /* FALLTHROUGH */
1749 default:
1750 if (el_insertstr(el, ins + 1) == -1)
1751 fatal("el_insertstr failed.");
1752 break;
1753 }
1754 }
1755 }
1756
1757 lf = el_line(el);
1758 /*
1759 * XXX should we really extend here? the user may not be done if
1760 * the filename is a directory.
1761 */
1762 if (g.gl_matchc == 1) {
1763 i = 0;
1764 if (!terminated)
1765 ins[i++] = quote;
1766 if (lastarg || *(lf->cursor) != ' ')
1767 ins[i++] = ' ';
1768 ins[i] = '\0';
1769 if (i > 0 && el_insertstr(el, ins) == -1)
1770 fatal("el_insertstr failed.");
1771 }
1772 xfree(tmp);
1773
1774 out:
1775 globfree(&g);
1776 return g.gl_matchc;
1777}
1778
1779/* tab-completion hook function, called via libedit */
1780static unsigned char
1781complete(EditLine *el, int ch)
1782{
1783 char **argv, *line, quote;
1784 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1785 const LineInfo *lf;
1786 struct complete_ctx *complete_ctx;
1787
1788 lf = el_line(el);
1789 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1790 fatal("%s: el_get failed", __func__);
1791
1792 /* Figure out which argument the cursor points to */
1793 cursor = lf->cursor - lf->buffer;
1794 line = (char *)xmalloc(cursor + 1);
1795 memcpy(line, lf->buffer, cursor);
1796 line[cursor] = '\0';
1797 argv = makeargv(line, &carg, 1, &quote, &terminated);
1798 xfree(line);
1799
1800 /* Get all the arguments on the line */
1801 len = lf->lastchar - lf->buffer;
1802 line = (char *)xmalloc(len + 1);
1803 memcpy(line, lf->buffer, len);
1804 line[len] = '\0';
1805 argv = makeargv(line, &argc, 1, NULL, NULL);
1806
1807 /* Ensure cursor is at EOL or a argument boundary */
1808 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1809 line[cursor] != '\n') {
1810 xfree(line);
1811 return ret;
1812 }
1813
1814 if (carg == 0) {
1815 /* Show all available commands */
1816 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1817 ret = CC_REDISPLAY;
1818 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1819 /* Handle the command parsing */
1820 if (complete_cmd_parse(el, argv[0], argc == carg,
1821 quote, terminated) != 0)
1822 ret = CC_REDISPLAY;
1823 } else if (carg >= 1) {
1824 /* Handle file parsing */
1825 int remote = complete_is_remote(argv[0]);
1826 char *filematch = NULL;
1827
1828 if (carg > 1 && line[cursor-1] != ' ')
1829 filematch = argv[carg - 1];
1830
1831 if (remote != 0 &&
1832 complete_match(el, complete_ctx->conn,
1833 *complete_ctx->remote_pathp, filematch,
1834 remote, carg == argc, quote, terminated) != 0)
1835 ret = CC_REDISPLAY;
1836 }
1837
1838 xfree(line);
1839 return ret;
1840}
1841
1471int 1842int
1472interactive_loop(struct sftp_conn *conn, char *file1, char *file2) 1843interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1473{ 1844{
1474 char *pwd; 1845 char *remote_path;
1475 char *dir = NULL; 1846 char *dir = NULL;
1476 char cmd[2048]; 1847 char cmd[2048];
1477 int err, interactive; 1848 int err, interactive;
@@ -1480,6 +1851,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1480 History *hl = NULL; 1851 History *hl = NULL;
1481 HistEvent hev; 1852 HistEvent hev;
1482 extern char *__progname; 1853 extern char *__progname;
1854 struct complete_ctx complete_ctx;
1483 1855
1484 if (!batchmode && isatty(STDIN_FILENO)) { 1856 if (!batchmode && isatty(STDIN_FILENO)) {
1485 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1857 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
@@ -1494,23 +1866,32 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1494 el_set(el, EL_TERMINAL, NULL); 1866 el_set(el, EL_TERMINAL, NULL);
1495 el_set(el, EL_SIGNAL, 1); 1867 el_set(el, EL_SIGNAL, 1);
1496 el_source(el, NULL); 1868 el_source(el, NULL);
1869
1870 /* Tab Completion */
1871 el_set(el, EL_ADDFN, "ftp-complete",
1872 "Context senstive argument completion", complete);
1873 complete_ctx.conn = conn;
1874 complete_ctx.remote_pathp = &remote_path;
1875 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1876 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1497 } 1877 }
1498#endif /* USE_LIBEDIT */ 1878#endif /* USE_LIBEDIT */
1499 1879
1500 pwd = do_realpath(conn, "."); 1880 remote_path = do_realpath(conn, ".");
1501 if (pwd == NULL) 1881 if (remote_path == NULL)
1502 fatal("Need cwd"); 1882 fatal("Need cwd");
1503 1883
1504 if (file1 != NULL) { 1884 if (file1 != NULL) {
1505 dir = xstrdup(file1); 1885 dir = xstrdup(file1);
1506 dir = make_absolute(dir, pwd); 1886 dir = make_absolute(dir, remote_path);
1507 1887
1508 if (remote_is_dir(conn, dir) && file2 == NULL) { 1888 if (remote_is_dir(conn, dir) && file2 == NULL) {
1509 printf("Changing to: %s\n", dir); 1889 printf("Changing to: %s\n", dir);
1510 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1890 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1511 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 1891 if (parse_dispatch_command(conn, cmd,
1892 &remote_path, 1) != 0) {
1512 xfree(dir); 1893 xfree(dir);
1513 xfree(pwd); 1894 xfree(remote_path);
1514 xfree(conn); 1895 xfree(conn);
1515 return (-1); 1896 return (-1);
1516 } 1897 }
@@ -1521,9 +1902,10 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1521 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1902 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1522 file2); 1903 file2);
1523 1904
1524 err = parse_dispatch_command(conn, cmd, &pwd, 1); 1905 err = parse_dispatch_command(conn, cmd,
1906 &remote_path, 1);
1525 xfree(dir); 1907 xfree(dir);
1526 xfree(pwd); 1908 xfree(remote_path);
1527 xfree(conn); 1909 xfree(conn);
1528 return (err); 1910 return (err);
1529 } 1911 }
@@ -1564,7 +1946,8 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1564 const char *line; 1946 const char *line;
1565 int count = 0; 1947 int count = 0;
1566 1948
1567 if ((line = el_gets(el, &count)) == NULL || count <= 0) { 1949 if ((line = el_gets(el, &count)) == NULL ||
1950 count <= 0) {
1568 printf("\n"); 1951 printf("\n");
1569 break; 1952 break;
1570 } 1953 }
@@ -1584,11 +1967,12 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1584 interrupted = 0; 1967 interrupted = 0;
1585 signal(SIGINT, cmd_interrupt); 1968 signal(SIGINT, cmd_interrupt);
1586 1969
1587 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1970 err = parse_dispatch_command(conn, cmd, &remote_path,
1971 batchmode);
1588 if (err != 0) 1972 if (err != 0)
1589 break; 1973 break;
1590 } 1974 }
1591 xfree(pwd); 1975 xfree(remote_path);
1592 xfree(conn); 1976 xfree(conn);
1593 1977
1594#ifdef USE_LIBEDIT 1978#ifdef USE_LIBEDIT