|
Lines 74-79
volatile sig_atomic_t interrupted = 0;
Link Here
|
| 74 |
/* I wish qsort() took a separate ctx for the comparison function...*/ |
74 |
/* I wish qsort() took a separate ctx for the comparison function...*/ |
| 75 |
int sort_flag; |
75 |
int sort_flag; |
| 76 |
|
76 |
|
|
|
77 |
/* Context used for commandline completion */ |
| 78 |
struct complete_ctx { |
| 79 |
struct sftp_conn *conn; |
| 80 |
char **remote_pathp; |
| 81 |
}; |
| 82 |
|
| 77 |
int remote_glob(struct sftp_conn *, const char *, int, |
83 |
int remote_glob(struct sftp_conn *, const char *, int, |
| 78 |
int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ |
84 |
int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ |
| 79 |
|
85 |
|
|
Lines 122-164
int remote_glob(struct sftp_conn *, cons
Link Here
|
| 122 |
struct CMD { |
128 |
struct CMD { |
| 123 |
const char *c; |
129 |
const char *c; |
| 124 |
const int n; |
130 |
const int n; |
|
|
131 |
const int t; |
| 125 |
}; |
132 |
}; |
| 126 |
|
133 |
|
|
|
134 |
/* Type of completion */ |
| 135 |
#define NOARGS 0 |
| 136 |
#define REMOTE 1 |
| 137 |
#define LOCAL 2 |
| 138 |
|
| 127 |
static const struct CMD cmds[] = { |
139 |
static const struct CMD cmds[] = { |
| 128 |
{ "bye", I_QUIT }, |
140 |
{ "bye", I_QUIT, NOARGS }, |
| 129 |
{ "cd", I_CHDIR }, |
141 |
{ "cd", I_CHDIR, REMOTE }, |
| 130 |
{ "chdir", I_CHDIR }, |
142 |
{ "chdir", I_CHDIR, REMOTE }, |
| 131 |
{ "chgrp", I_CHGRP }, |
143 |
{ "chgrp", I_CHGRP, REMOTE }, |
| 132 |
{ "chmod", I_CHMOD }, |
144 |
{ "chmod", I_CHMOD, REMOTE }, |
| 133 |
{ "chown", I_CHOWN }, |
145 |
{ "chown", I_CHOWN, REMOTE }, |
| 134 |
{ "df", I_DF }, |
146 |
{ "df", I_DF, REMOTE }, |
| 135 |
{ "dir", I_LS }, |
147 |
{ "dir", I_LS, REMOTE }, |
| 136 |
{ "exit", I_QUIT }, |
148 |
{ "exit", I_QUIT, NOARGS }, |
| 137 |
{ "get", I_GET }, |
149 |
{ "get", I_GET, REMOTE }, |
| 138 |
{ "mget", I_GET }, |
150 |
{ "help", I_HELP, NOARGS }, |
| 139 |
{ "help", I_HELP }, |
151 |
{ "lcd", I_LCHDIR, LOCAL }, |
| 140 |
{ "lcd", I_LCHDIR }, |
152 |
{ "lchdir", I_LCHDIR, LOCAL }, |
| 141 |
{ "lchdir", I_LCHDIR }, |
153 |
{ "lls", I_LLS, LOCAL }, |
| 142 |
{ "lls", I_LLS }, |
154 |
{ "lmkdir", I_LMKDIR, LOCAL }, |
| 143 |
{ "lmkdir", I_LMKDIR }, |
155 |
{ "ln", I_SYMLINK, REMOTE }, |
| 144 |
{ "ln", I_SYMLINK }, |
156 |
{ "lpwd", I_LPWD, LOCAL }, |
| 145 |
{ "lpwd", I_LPWD }, |
157 |
{ "ls", I_LS, REMOTE }, |
| 146 |
{ "ls", I_LS }, |
158 |
{ "lumask", I_LUMASK, NOARGS }, |
| 147 |
{ "lumask", I_LUMASK }, |
159 |
{ "mkdir", I_MKDIR, REMOTE }, |
| 148 |
{ "mkdir", I_MKDIR }, |
160 |
{ "progress", I_PROGRESS, NOARGS }, |
| 149 |
{ "progress", I_PROGRESS }, |
161 |
{ "put", I_PUT, LOCAL }, |
| 150 |
{ "put", I_PUT }, |
162 |
{ "pwd", I_PWD, REMOTE }, |
| 151 |
{ "mput", I_PUT }, |
163 |
{ "quit", I_QUIT, NOARGS }, |
| 152 |
{ "pwd", I_PWD }, |
164 |
{ "rename", I_RENAME, REMOTE }, |
| 153 |
{ "quit", I_QUIT }, |
165 |
{ "rm", I_RM, REMOTE }, |
| 154 |
{ "rename", I_RENAME }, |
166 |
{ "rmdir", I_RMDIR, REMOTE }, |
| 155 |
{ "rm", I_RM }, |
167 |
{ "symlink", I_SYMLINK, REMOTE }, |
| 156 |
{ "rmdir", I_RMDIR }, |
168 |
{ "version", I_VERSION, NOARGS }, |
| 157 |
{ "symlink", I_SYMLINK }, |
169 |
{ "!", I_SHELL, NOARGS }, |
| 158 |
{ "version", I_VERSION }, |
170 |
{ "?", I_HELP, NOARGS }, |
| 159 |
{ "!", I_SHELL }, |
171 |
{ NULL, -1, -1 } |
| 160 |
{ "?", I_HELP }, |
|
|
| 161 |
{ NULL, -1} |
| 162 |
}; |
172 |
}; |
| 163 |
|
173 |
|
| 164 |
int interactive_loop(struct sftp_conn *, char *file1, char *file2); |
174 |
int interactive_loop(struct sftp_conn *, char *file1, char *file2); |
|
Lines 909-920
undo_glob_escape(char *s)
Link Here
|
| 909 |
* Split a string into an argument vector using sh(1)-style quoting, |
919 |
* Split a string into an argument vector using sh(1)-style quoting, |
| 910 |
* comment and escaping rules, but with some tweaks to handle glob(3) |
920 |
* comment and escaping rules, but with some tweaks to handle glob(3) |
| 911 |
* wildcards. |
921 |
* wildcards. |
|
|
922 |
* The "sloppy" flag allows for recovery from missing terminating quote, for |
| 923 |
* use in parsing incomplete commandlines during tab autocompletion. |
| 924 |
* |
| 912 |
* Returns NULL on error or a NULL-terminated array of arguments. |
925 |
* Returns NULL on error or a NULL-terminated array of arguments. |
|
|
926 |
* |
| 927 |
* If "lastquote" is not NULL, the quoting character used for the last |
| 928 |
* argument is placed in *lastquote ("\0", "'" or "\""). |
| 929 |
* |
| 930 |
* If "terminated" is not NULL, *terminated will be set to 1 when the |
| 931 |
* last argument's quote has been properly terminated or 0 otherwise. |
| 932 |
* This parameter is only of use if "sloppy" is set. |
| 913 |
*/ |
933 |
*/ |
| 914 |
#define MAXARGS 128 |
934 |
#define MAXARGS 128 |
| 915 |
#define MAXARGLEN 8192 |
935 |
#define MAXARGLEN 8192 |
| 916 |
static char ** |
936 |
static char ** |
| 917 |
makeargv(const char *arg, int *argcp) |
937 |
makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, |
|
|
938 |
u_int *terminated) |
| 918 |
{ |
939 |
{ |
| 919 |
int argc, quot; |
940 |
int argc, quot; |
| 920 |
size_t i, j; |
941 |
size_t i, j; |
|
Lines 928-933
makeargv(const char *arg, int *argcp)
Link Here
|
| 928 |
error("string too long"); |
949 |
error("string too long"); |
| 929 |
return NULL; |
950 |
return NULL; |
| 930 |
} |
951 |
} |
|
|
952 |
if (terminated != NULL) |
| 953 |
*terminated = 1; |
| 954 |
if (lastquote != NULL) |
| 955 |
*lastquote = '\0'; |
| 931 |
state = MA_START; |
956 |
state = MA_START; |
| 932 |
i = j = 0; |
957 |
i = j = 0; |
| 933 |
for (;;) { |
958 |
for (;;) { |
|
Lines 944-949
makeargv(const char *arg, int *argcp)
Link Here
|
| 944 |
if (state == MA_START) { |
969 |
if (state == MA_START) { |
| 945 |
argv[argc] = argvs + j; |
970 |
argv[argc] = argvs + j; |
| 946 |
state = q; |
971 |
state = q; |
|
|
972 |
if (lastquote != NULL) |
| 973 |
*lastquote = arg[i]; |
| 947 |
} else if (state == MA_UNQUOTED) |
974 |
} else if (state == MA_UNQUOTED) |
| 948 |
state = q; |
975 |
state = q; |
| 949 |
else if (state == q) |
976 |
else if (state == q) |
|
Lines 980-985
makeargv(const char *arg, int *argcp)
Link Here
|
| 980 |
if (state == MA_START) { |
1007 |
if (state == MA_START) { |
| 981 |
argv[argc] = argvs + j; |
1008 |
argv[argc] = argvs + j; |
| 982 |
state = MA_UNQUOTED; |
1009 |
state = MA_UNQUOTED; |
|
|
1010 |
if (lastquote != NULL) |
| 1011 |
*lastquote = '\0'; |
| 983 |
} |
1012 |
} |
| 984 |
if (arg[i + 1] == '?' || arg[i + 1] == '[' || |
1013 |
if (arg[i + 1] == '?' || arg[i + 1] == '[' || |
| 985 |
arg[i + 1] == '*' || arg[i + 1] == '\\') { |
1014 |
arg[i + 1] == '*' || arg[i + 1] == '\\') { |
|
Lines 1005-1010
makeargv(const char *arg, int *argcp)
Link Here
|
| 1005 |
goto string_done; |
1034 |
goto string_done; |
| 1006 |
} else if (arg[i] == '\0') { |
1035 |
} else if (arg[i] == '\0') { |
| 1007 |
if (state == MA_SQUOTE || state == MA_DQUOTE) { |
1036 |
if (state == MA_SQUOTE || state == MA_DQUOTE) { |
|
|
1037 |
if (sloppy) { |
| 1038 |
state = MA_UNQUOTED; |
| 1039 |
if (terminated != NULL) |
| 1040 |
*terminated = 0; |
| 1041 |
goto string_done; |
| 1042 |
} |
| 1008 |
error("Unterminated quoted argument"); |
1043 |
error("Unterminated quoted argument"); |
| 1009 |
return NULL; |
1044 |
return NULL; |
| 1010 |
} |
1045 |
} |
|
Lines 1018-1023
makeargv(const char *arg, int *argcp)
Link Here
|
| 1018 |
if (state == MA_START) { |
1053 |
if (state == MA_START) { |
| 1019 |
argv[argc] = argvs + j; |
1054 |
argv[argc] = argvs + j; |
| 1020 |
state = MA_UNQUOTED; |
1055 |
state = MA_UNQUOTED; |
|
|
1056 |
if (lastquote != NULL) |
| 1057 |
*lastquote = '\0'; |
| 1021 |
} |
1058 |
} |
| 1022 |
if ((state == MA_SQUOTE || state == MA_DQUOTE) && |
1059 |
if ((state == MA_SQUOTE || state == MA_DQUOTE) && |
| 1023 |
(arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { |
1060 |
(arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { |
|
Lines 1040-1047
makeargv(const char *arg, int *argcp)
Link Here
|
| 1040 |
} |
1077 |
} |
| 1041 |
|
1078 |
|
| 1042 |
static int |
1079 |
static int |
| 1043 |
parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag, |
1080 |
parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, |
| 1044 |
unsigned long *n_arg, char **path1, char **path2) |
1081 |
int *hflag, unsigned long *n_arg, char **path1, char **path2) |
| 1045 |
{ |
1082 |
{ |
| 1046 |
const char *cmd, *cp = *cpp; |
1083 |
const char *cmd, *cp = *cpp; |
| 1047 |
char *cp2, **argv; |
1084 |
char *cp2, **argv; |
|
Lines 1063-1069
parse_args(const char **cpp, int *pflag,
Link Here
|
| 1063 |
cp++; |
1100 |
cp++; |
| 1064 |
} |
1101 |
} |
| 1065 |
|
1102 |
|
| 1066 |
if ((argv = makeargv(cp, &argc)) == NULL) |
1103 |
if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) |
| 1067 |
return -1; |
1104 |
return -1; |
| 1068 |
|
1105 |
|
| 1069 |
/* Figure out which command we have */ |
1106 |
/* Figure out which command we have */ |
|
Lines 1443-1452
prompt(EditLine *el)
Link Here
|
| 1443 |
return ("sftp> "); |
1480 |
return ("sftp> "); |
| 1444 |
} |
1481 |
} |
| 1445 |
|
1482 |
|
|
|
1483 |
/* Display entries in 'list' after skipping the first 'len' chars */ |
| 1484 |
static void |
| 1485 |
complete_display(char **list, u_int len) |
| 1486 |
{ |
| 1487 |
u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; |
| 1488 |
struct winsize ws; |
| 1489 |
char *tmp; |
| 1490 |
|
| 1491 |
/* Count entries for sort and find longest */ |
| 1492 |
for (y = 0; list[y]; y++) |
| 1493 |
m = MAX(m, strlen(list[y])); |
| 1494 |
|
| 1495 |
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) |
| 1496 |
width = ws.ws_col; |
| 1497 |
|
| 1498 |
m = m > len ? m - len : 0; |
| 1499 |
columns = width / (m + 2); |
| 1500 |
columns = MAX(columns, 1); |
| 1501 |
colspace = width / columns; |
| 1502 |
colspace = MIN(colspace, width); |
| 1503 |
|
| 1504 |
printf("\n"); |
| 1505 |
m = 1; |
| 1506 |
for (y = 0; list[y]; y++) { |
| 1507 |
llen = strlen(list[y]); |
| 1508 |
tmp = llen > len ? list[y] + len : ""; |
| 1509 |
printf("%-*s", colspace, tmp); |
| 1510 |
if (m >= columns) { |
| 1511 |
printf("\n"); |
| 1512 |
m = 1; |
| 1513 |
} else |
| 1514 |
m++; |
| 1515 |
} |
| 1516 |
printf("\n"); |
| 1517 |
} |
| 1518 |
|
| 1519 |
/* |
| 1520 |
* Given a "list" of words that begin with a common prefix of "word", |
| 1521 |
* attempt to find an autocompletion to extends "word" by the next |
| 1522 |
* characters common to all entries in "list". |
| 1523 |
*/ |
| 1524 |
static char * |
| 1525 |
complete_ambiguous(const char *word, char **list, size_t count) |
| 1526 |
{ |
| 1527 |
if (word == NULL) |
| 1528 |
return NULL; |
| 1529 |
|
| 1530 |
if (count > 0) { |
| 1531 |
u_int y, matchlen = strlen(list[0]); |
| 1532 |
|
| 1533 |
/* Find length of common stem */ |
| 1534 |
for (y = 1; list[y]; y++) { |
| 1535 |
u_int x; |
| 1536 |
|
| 1537 |
for (x = 0; x < matchlen; x++) |
| 1538 |
if (list[0][x] != list[y][x]) |
| 1539 |
break; |
| 1540 |
|
| 1541 |
matchlen = x; |
| 1542 |
} |
| 1543 |
|
| 1544 |
if (matchlen > strlen(word)) { |
| 1545 |
char *tmp = xstrdup(list[0]); |
| 1546 |
|
| 1547 |
tmp[matchlen] = NULL; |
| 1548 |
return tmp; |
| 1549 |
} |
| 1550 |
} |
| 1551 |
|
| 1552 |
return xstrdup(word); |
| 1553 |
} |
| 1554 |
|
| 1555 |
/* Autocomplete a sftp command */ |
| 1556 |
static int |
| 1557 |
complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, |
| 1558 |
int terminated) |
| 1559 |
{ |
| 1560 |
u_int y, count = 0, cmdlen, tmplen; |
| 1561 |
char *tmp, **list, argterm[3]; |
| 1562 |
const LineInfo *lf; |
| 1563 |
|
| 1564 |
list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); |
| 1565 |
|
| 1566 |
/* No command specified: display all available commands */ |
| 1567 |
if (cmd == NULL) { |
| 1568 |
for (y = 0; cmds[y].c; y++) |
| 1569 |
list[count++] = xstrdup(cmds[y].c); |
| 1570 |
|
| 1571 |
list[count] = NULL; |
| 1572 |
complete_display(list, 0); |
| 1573 |
|
| 1574 |
for (y = 0; list[y] != NULL; y++) |
| 1575 |
xfree(list[y]); |
| 1576 |
xfree(list); |
| 1577 |
return count; |
| 1578 |
} |
| 1579 |
|
| 1580 |
/* Prepare subset of commands that start with "cmd" */ |
| 1581 |
cmdlen = strlen(cmd); |
| 1582 |
for (y = 0; cmds[y].c; y++) { |
| 1583 |
if (!strncasecmp(cmd, cmds[y].c, cmdlen)) |
| 1584 |
list[count++] = xstrdup(cmds[y].c); |
| 1585 |
} |
| 1586 |
list[count] = NULL; |
| 1587 |
|
| 1588 |
if (count == 0) |
| 1589 |
return 0; |
| 1590 |
|
| 1591 |
/* Complete ambigious command */ |
| 1592 |
tmp = complete_ambiguous(cmd, list, count); |
| 1593 |
if (count > 1) |
| 1594 |
complete_display(list, 0); |
| 1595 |
|
| 1596 |
for (y = 0; list[y]; y++) |
| 1597 |
xfree(list[y]); |
| 1598 |
xfree(list); |
| 1599 |
|
| 1600 |
if (tmp != NULL) { |
| 1601 |
tmplen = strlen(tmp); |
| 1602 |
cmdlen = strlen(cmd); |
| 1603 |
/* If cmd may be extended then do so */ |
| 1604 |
if (tmplen > cmdlen) |
| 1605 |
if (el_insertstr(el, tmp + cmdlen) == -1) |
| 1606 |
fatal("el_insertstr failed."); |
| 1607 |
lf = el_line(el); |
| 1608 |
/* Terminate argument cleanly */ |
| 1609 |
if (count == 1) { |
| 1610 |
y = 0; |
| 1611 |
if (!terminated) |
| 1612 |
argterm[y++] = quote; |
| 1613 |
if (lastarg || *(lf->cursor) != ' ') |
| 1614 |
argterm[y++] = ' '; |
| 1615 |
argterm[y] = '\0'; |
| 1616 |
if (y > 0 && el_insertstr(el, argterm) == -1) |
| 1617 |
fatal("el_insertstr failed."); |
| 1618 |
} |
| 1619 |
xfree(tmp); |
| 1620 |
} |
| 1621 |
|
| 1622 |
return count; |
| 1623 |
} |
| 1624 |
|
| 1625 |
/* |
| 1626 |
* Determine whether a particular sftp command's arguments (if any) |
| 1627 |
* represent local or remote files. |
| 1628 |
*/ |
| 1629 |
static int |
| 1630 |
complete_is_remote(char *cmd) { |
| 1631 |
int i; |
| 1632 |
|
| 1633 |
if (cmd == NULL) |
| 1634 |
return -1; |
| 1635 |
|
| 1636 |
for (i = 0; cmds[i].c; i++) { |
| 1637 |
if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) |
| 1638 |
return cmds[i].t; |
| 1639 |
} |
| 1640 |
|
| 1641 |
return -1; |
| 1642 |
} |
| 1643 |
|
| 1644 |
/* Autocomplete a filename "file" */ |
| 1645 |
static int |
| 1646 |
complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, |
| 1647 |
char *file, int remote, int lastarg, char quote, int terminated) |
| 1648 |
{ |
| 1649 |
glob_t g; |
| 1650 |
char *tmp, *tmp2, ins[3]; |
| 1651 |
u_int i, hadglob, pwdlen, len, tmplen, filelen; |
| 1652 |
const LineInfo *lf; |
| 1653 |
|
| 1654 |
/* Glob from "file" location */ |
| 1655 |
if (file == NULL) |
| 1656 |
tmp = xstrdup("*"); |
| 1657 |
else |
| 1658 |
xasprintf(&tmp, "%s*", file); |
| 1659 |
|
| 1660 |
memset(&g, 0, sizeof(g)); |
| 1661 |
if (remote != LOCAL) { |
| 1662 |
tmp = make_absolute(tmp, remote_path); |
| 1663 |
remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); |
| 1664 |
} else |
| 1665 |
glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); |
| 1666 |
|
| 1667 |
/* Determine length of pwd so we can trim completion display */ |
| 1668 |
for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { |
| 1669 |
/* Terminate counting on first unescaped glob metacharacter */ |
| 1670 |
if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { |
| 1671 |
if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') |
| 1672 |
hadglob = 1; |
| 1673 |
break; |
| 1674 |
} |
| 1675 |
if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') |
| 1676 |
tmplen++; |
| 1677 |
if (tmp[tmplen] == '/') |
| 1678 |
pwdlen = tmplen + 1; /* track last seen '/' */ |
| 1679 |
} |
| 1680 |
xfree(tmp); |
| 1681 |
|
| 1682 |
if (g.gl_matchc == 0) |
| 1683 |
goto out; |
| 1684 |
|
| 1685 |
if (g.gl_matchc > 1) |
| 1686 |
complete_display(g.gl_pathv, pwdlen); |
| 1687 |
|
| 1688 |
tmp = NULL; |
| 1689 |
/* Don't try to extend globs */ |
| 1690 |
if (file == NULL || hadglob) |
| 1691 |
goto out; |
| 1692 |
|
| 1693 |
tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); |
| 1694 |
tmp = path_strip(tmp2, remote_path); |
| 1695 |
xfree(tmp2); |
| 1696 |
|
| 1697 |
if (tmp == NULL) |
| 1698 |
goto out; |
| 1699 |
|
| 1700 |
tmplen = strlen(tmp); |
| 1701 |
filelen = strlen(file); |
| 1702 |
|
| 1703 |
if (tmplen > filelen) { |
| 1704 |
tmp2 = tmp + filelen; |
| 1705 |
len = strlen(tmp2); |
| 1706 |
/* quote argument on way out */ |
| 1707 |
for (i = 0; i < len; i++) { |
| 1708 |
ins[0] = '\\'; |
| 1709 |
ins[1] = tmp2[i]; |
| 1710 |
ins[2] = '\0'; |
| 1711 |
switch (tmp2[i]) { |
| 1712 |
case '\'': |
| 1713 |
case '"': |
| 1714 |
case '\\': |
| 1715 |
case '\t': |
| 1716 |
case ' ': |
| 1717 |
if (quote == '\0' || tmp2[i] == quote) { |
| 1718 |
if (el_insertstr(el, ins) == -1) |
| 1719 |
fatal("el_insertstr " |
| 1720 |
"failed."); |
| 1721 |
break; |
| 1722 |
} |
| 1723 |
/* FALLTHROUGH */ |
| 1724 |
default: |
| 1725 |
if (el_insertstr(el, ins + 1) == -1) |
| 1726 |
fatal("el_insertstr failed."); |
| 1727 |
break; |
| 1728 |
} |
| 1729 |
} |
| 1730 |
} |
| 1731 |
|
| 1732 |
lf = el_line(el); |
| 1733 |
/* |
| 1734 |
* XXX should we really extend here? the user may not be done if |
| 1735 |
* the filename is a directory. |
| 1736 |
*/ |
| 1737 |
if (g.gl_matchc == 1) { |
| 1738 |
i = 0; |
| 1739 |
if (!terminated) |
| 1740 |
ins[i++] = quote; |
| 1741 |
if (lastarg || *(lf->cursor) != ' ') |
| 1742 |
ins[i++] = ' '; |
| 1743 |
ins[i] = '\0'; |
| 1744 |
if (i > 0 && el_insertstr(el, ins) == -1) |
| 1745 |
fatal("el_insertstr failed."); |
| 1746 |
} |
| 1747 |
xfree(tmp); |
| 1748 |
|
| 1749 |
out: |
| 1750 |
globfree(&g); |
| 1751 |
return g.gl_matchc; |
| 1752 |
} |
| 1753 |
|
| 1754 |
/* tab-completion hook function, called via libedit */ |
| 1755 |
static unsigned char |
| 1756 |
complete(EditLine *el, int ch) |
| 1757 |
{ |
| 1758 |
char **argv, *line, quote; |
| 1759 |
u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; |
| 1760 |
const LineInfo *lf; |
| 1761 |
struct complete_ctx *complete_ctx; |
| 1762 |
|
| 1763 |
lf = el_line(el); |
| 1764 |
if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) |
| 1765 |
fatal("%s: el_get failed", __func__); |
| 1766 |
|
| 1767 |
/* Figure out which argument the cursor points to */ |
| 1768 |
cursor = lf->cursor - lf->buffer; |
| 1769 |
line = (char *)xmalloc(cursor + 1); |
| 1770 |
memcpy(line, lf->buffer, cursor); |
| 1771 |
line[cursor] = '\0'; |
| 1772 |
argv = makeargv(line, &carg, 1, "e, &terminated); |
| 1773 |
xfree(line); |
| 1774 |
|
| 1775 |
/* Get all the arguments on the line */ |
| 1776 |
len = lf->lastchar - lf->buffer; |
| 1777 |
line = (char *)xmalloc(len + 1); |
| 1778 |
memcpy(line, lf->buffer, len); |
| 1779 |
line[len] = '\0'; |
| 1780 |
argv = makeargv(line, &argc, 1, NULL, NULL); |
| 1781 |
|
| 1782 |
/* Ensure cursor is at EOL or a argument boundary */ |
| 1783 |
if (line[cursor] != ' ' && line[cursor] != '\0' && |
| 1784 |
line[cursor] != '\n') { |
| 1785 |
xfree(line); |
| 1786 |
return ret; |
| 1787 |
} |
| 1788 |
|
| 1789 |
if (carg == 0) { |
| 1790 |
/* Show all available commands */ |
| 1791 |
complete_cmd_parse(el, NULL, argc == carg, '\0', 1); |
| 1792 |
ret = CC_REDISPLAY; |
| 1793 |
} else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { |
| 1794 |
/* Handle the command parsing */ |
| 1795 |
if (complete_cmd_parse(el, argv[0], argc == carg, |
| 1796 |
quote, terminated) != 0) |
| 1797 |
ret = CC_REDISPLAY; |
| 1798 |
} else if (carg >= 1) { |
| 1799 |
/* Handle file parsing */ |
| 1800 |
int remote = complete_is_remote(argv[0]); |
| 1801 |
char *filematch = NULL; |
| 1802 |
|
| 1803 |
if (carg > 1 && line[cursor-1] != ' ') |
| 1804 |
filematch = argv[carg - 1]; |
| 1805 |
|
| 1806 |
if (remote != 0 && |
| 1807 |
complete_match(el, complete_ctx->conn, |
| 1808 |
*complete_ctx->remote_pathp, filematch, |
| 1809 |
remote, carg == argc, quote, terminated) != 0) |
| 1810 |
ret = CC_REDISPLAY; |
| 1811 |
} |
| 1812 |
|
| 1813 |
xfree(line); |
| 1814 |
return ret; |
| 1815 |
} |
| 1816 |
|
| 1446 |
int |
1817 |
int |
| 1447 |
interactive_loop(struct sftp_conn *conn, char *file1, char *file2) |
1818 |
interactive_loop(struct sftp_conn *conn, char *file1, char *file2) |
| 1448 |
{ |
1819 |
{ |
| 1449 |
char *pwd; |
1820 |
char *remote_path; |
| 1450 |
char *dir = NULL; |
1821 |
char *dir = NULL; |
| 1451 |
char cmd[2048]; |
1822 |
char cmd[2048]; |
| 1452 |
int err, interactive; |
1823 |
int err, interactive; |
|
Lines 1454-1459
interactive_loop(struct sftp_conn *conn,
Link Here
|
| 1454 |
History *hl = NULL; |
1825 |
History *hl = NULL; |
| 1455 |
HistEvent hev; |
1826 |
HistEvent hev; |
| 1456 |
extern char *__progname; |
1827 |
extern char *__progname; |
|
|
1828 |
struct complete_ctx complete_ctx; |
| 1457 |
|
1829 |
|
| 1458 |
if (!batchmode && isatty(STDIN_FILENO)) { |
1830 |
if (!batchmode && isatty(STDIN_FILENO)) { |
| 1459 |
if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) |
1831 |
if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) |
|
Lines 1468-1489
interactive_loop(struct sftp_conn *conn,
Link Here
|
| 1468 |
el_set(el, EL_TERMINAL, NULL); |
1840 |
el_set(el, EL_TERMINAL, NULL); |
| 1469 |
el_set(el, EL_SIGNAL, 1); |
1841 |
el_set(el, EL_SIGNAL, 1); |
| 1470 |
el_source(el, NULL); |
1842 |
el_source(el, NULL); |
|
|
1843 |
|
| 1844 |
/* Tab Completion */ |
| 1845 |
el_set(el, EL_ADDFN, "ftp-complete", |
| 1846 |
"Context senstive argument completion", complete); |
| 1847 |
complete_ctx.conn = conn; |
| 1848 |
complete_ctx.remote_pathp = &remote_path; |
| 1849 |
el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); |
| 1850 |
el_set(el, EL_BIND, "^I", "ftp-complete", NULL); |
| 1471 |
} |
1851 |
} |
| 1472 |
|
1852 |
|
| 1473 |
pwd = do_realpath(conn, "."); |
1853 |
remote_path = do_realpath(conn, "."); |
| 1474 |
if (pwd == NULL) |
1854 |
if (remote_path == NULL) |
| 1475 |
fatal("Need cwd"); |
1855 |
fatal("Need cwd"); |
| 1476 |
|
1856 |
|
| 1477 |
if (file1 != NULL) { |
1857 |
if (file1 != NULL) { |
| 1478 |
dir = xstrdup(file1); |
1858 |
dir = xstrdup(file1); |
| 1479 |
dir = make_absolute(dir, pwd); |
1859 |
dir = make_absolute(dir, remote_path); |
| 1480 |
|
1860 |
|
| 1481 |
if (remote_is_dir(conn, dir) && file2 == NULL) { |
1861 |
if (remote_is_dir(conn, dir) && file2 == NULL) { |
| 1482 |
printf("Changing to: %s\n", dir); |
1862 |
printf("Changing to: %s\n", dir); |
| 1483 |
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); |
1863 |
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); |
| 1484 |
if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { |
1864 |
if (parse_dispatch_command(conn, cmd, |
|
|
1865 |
&remote_path, 1) != 0) { |
| 1485 |
xfree(dir); |
1866 |
xfree(dir); |
| 1486 |
xfree(pwd); |
1867 |
xfree(remote_path); |
| 1487 |
xfree(conn); |
1868 |
xfree(conn); |
| 1488 |
return (-1); |
1869 |
return (-1); |
| 1489 |
} |
1870 |
} |
|
Lines 1494-1502
interactive_loop(struct sftp_conn *conn,
Link Here
|
| 1494 |
snprintf(cmd, sizeof cmd, "get %s %s", dir, |
1875 |
snprintf(cmd, sizeof cmd, "get %s %s", dir, |
| 1495 |
file2); |
1876 |
file2); |
| 1496 |
|
1877 |
|
| 1497 |
err = parse_dispatch_command(conn, cmd, &pwd, 1); |
1878 |
err = parse_dispatch_command(conn, cmd, |
|
|
1879 |
&remote_path, 1); |
| 1498 |
xfree(dir); |
1880 |
xfree(dir); |
| 1499 |
xfree(pwd); |
1881 |
xfree(remote_path); |
| 1500 |
xfree(conn); |
1882 |
xfree(conn); |
| 1501 |
return (err); |
1883 |
return (err); |
| 1502 |
} |
1884 |
} |
|
Lines 1530-1536
interactive_loop(struct sftp_conn *conn,
Link Here
|
| 1530 |
printf("\n"); |
1912 |
printf("\n"); |
| 1531 |
} |
1913 |
} |
| 1532 |
} else { |
1914 |
} else { |
| 1533 |
if ((line = el_gets(el, &count)) == NULL || count <= 0) { |
1915 |
if ((line = el_gets(el, &count)) == NULL || |
|
|
1916 |
count <= 0) { |
| 1534 |
printf("\n"); |
1917 |
printf("\n"); |
| 1535 |
break; |
1918 |
break; |
| 1536 |
} |
1919 |
} |
|
Lines 1549-1559
interactive_loop(struct sftp_conn *conn,
Link Here
|
| 1549 |
interrupted = 0; |
1932 |
interrupted = 0; |
| 1550 |
signal(SIGINT, cmd_interrupt); |
1933 |
signal(SIGINT, cmd_interrupt); |
| 1551 |
|
1934 |
|
| 1552 |
err = parse_dispatch_command(conn, cmd, &pwd, batchmode); |
1935 |
err = parse_dispatch_command(conn, cmd, &remote_path, |
|
|
1936 |
batchmode); |
| 1553 |
if (err != 0) |
1937 |
if (err != 0) |
| 1554 |
break; |
1938 |
break; |
| 1555 |
} |
1939 |
} |
| 1556 |
xfree(pwd); |
1940 |
xfree(remote_path); |
| 1557 |
xfree(conn); |
1941 |
xfree(conn); |
| 1558 |
|
1942 |
|
| 1559 |
if (el != NULL) |
1943 |
if (el != NULL) |