blob: c236f6dac20ea2902405e4a449ac3e42863c493e [file] [log] [blame]
Damien Miller33804262001-02-04 23:20:18 +11001/*
2 * Copyright (c) 2001 Damien Miller. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25/* XXX: finish implementation of all commands */
26/* XXX: do fnmatch() instead of using raw pathname */
Damien Millerd7686fd2001-02-10 00:40:03 +110027/* XXX: globbed ls */
Damien Miller33804262001-02-04 23:20:18 +110028/* XXX: recursive operations */
29
30#include "includes.h"
Ben Lindstrom66904942001-02-15 03:19:56 +000031RCSID("$OpenBSD: sftp-int.c,v 1.21 2001/02/12 20:53:33 stevesk Exp $");
Damien Miller33804262001-02-04 23:20:18 +110032
33#include "buffer.h"
34#include "xmalloc.h"
35#include "log.h"
36#include "pathnames.h"
37
38#include "sftp.h"
39#include "sftp-common.h"
40#include "sftp-client.h"
41#include "sftp-int.h"
42
43/* Seperators for interactive commands */
44#define WHITESPACE " \t\r\n"
45
46/* Commands for interactive mode */
47#define I_CHDIR 1
48#define I_CHGRP 2
49#define I_CHMOD 3
50#define I_CHOWN 4
51#define I_GET 5
52#define I_HELP 6
53#define I_LCHDIR 7
54#define I_LLS 8
55#define I_LMKDIR 9
56#define I_LPWD 10
57#define I_LS 11
58#define I_LUMASK 12
59#define I_MKDIR 13
60#define I_PUT 14
61#define I_PWD 15
62#define I_QUIT 16
63#define I_RENAME 17
64#define I_RM 18
65#define I_RMDIR 19
66#define I_SHELL 20
67
68struct CMD {
Damien Miller33804262001-02-04 23:20:18 +110069 const char *c;
Kevin Steves62c45db2001-02-05 13:42:43 +000070 const int n;
Damien Miller33804262001-02-04 23:20:18 +110071};
72
73const struct CMD cmds[] = {
Damien Millerd7686fd2001-02-10 00:40:03 +110074 { "cd", I_CHDIR },
75 { "chdir", I_CHDIR },
76 { "chgrp", I_CHGRP },
77 { "chmod", I_CHMOD },
78 { "chown", I_CHOWN },
79 { "dir", I_LS },
80 { "exit", I_QUIT },
81 { "get", I_GET },
82 { "help", I_HELP },
83 { "lcd", I_LCHDIR },
84 { "lchdir", I_LCHDIR },
85 { "lls", I_LLS },
86 { "lmkdir", I_LMKDIR },
87 { "lpwd", I_LPWD },
88 { "ls", I_LS },
89 { "lumask", I_LUMASK },
90 { "mkdir", I_MKDIR },
91 { "put", I_PUT },
92 { "pwd", I_PWD },
93 { "quit", I_QUIT },
94 { "rename", I_RENAME },
95 { "rm", I_RM },
96 { "rmdir", I_RMDIR },
Kevin Steves62c45db2001-02-05 13:42:43 +000097 { "!", I_SHELL },
98 { "?", I_HELP },
99 { NULL, -1}
Damien Miller33804262001-02-04 23:20:18 +1100100};
101
102void
103help(void)
104{
105 printf("Available commands:\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100106 printf("cd path Change remote directory to 'path'\n");
107 printf("lcd path Change local directory to 'path'\n");
108 printf("chgrp grp path Change group of file 'path' to 'grp'\n");
109 printf("chmod mode path Change permissions of file 'path' to 'mode'\n");
110 printf("chown own path Change owner of file 'path' to 'own'\n");
111 printf("help Display this help text\n");
112 printf("get remote-path [local-path] Download file\n");
113 printf("lls [ls-options [path]] Display local directory listing\n");
114 printf("lmkdir path Create local directory\n");
115 printf("lpwd Print local working directory\n");
116 printf("ls [path] Display remote directory listing\n");
117 printf("lumask umask Set local umask to 'umask'\n");
118 printf("mkdir path Create remote directory\n");
119 printf("put local-path [remote-path] Upload file\n");
120 printf("pwd Display remote working directory\n");
121 printf("exit Quit sftp\n");
122 printf("quit Quit sftp\n");
123 printf("rename oldpath newpath Rename remote file\n");
124 printf("rmdir path Remove remote directory\n");
125 printf("rm path Delete remote file\n");
Damien Miller33804262001-02-04 23:20:18 +1100126 printf("!command Execute 'command' in local shell\n");
127 printf("! Escape to local shell\n");
Damien Millerd7686fd2001-02-10 00:40:03 +1100128 printf("? Synonym for help\n");
Damien Miller33804262001-02-04 23:20:18 +1100129}
130
131void
132local_do_shell(const char *args)
133{
134 int ret, status;
135 char *shell;
136 pid_t pid;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000137
Damien Miller33804262001-02-04 23:20:18 +1100138 if (!*args)
139 args = NULL;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000140
Damien Miller33804262001-02-04 23:20:18 +1100141 if ((shell = getenv("SHELL")) == NULL)
142 shell = _PATH_BSHELL;
143
144 if ((pid = fork()) == -1)
145 fatal("Couldn't fork: %s", strerror(errno));
146
147 if (pid == 0) {
148 /* XXX: child has pipe fds to ssh subproc open - issue? */
149 if (args) {
150 debug3("Executing %s -c \"%s\"", shell, args);
151 ret = execl(shell, shell, "-c", args, NULL);
152 } else {
153 debug3("Executing %s", shell);
154 ret = execl(shell, shell, NULL);
155 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000156 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
Damien Miller33804262001-02-04 23:20:18 +1100157 strerror(errno));
158 _exit(1);
159 }
160 if (waitpid(pid, &status, 0) == -1)
161 fatal("Couldn't wait for child: %s", strerror(errno));
162 if (!WIFEXITED(status))
163 error("Shell exited abormally");
164 else if (WEXITSTATUS(status))
165 error("Shell exited with status %d", WEXITSTATUS(status));
166}
167
Kevin Stevesef4eea92001-02-05 12:42:17 +0000168void
Damien Miller33804262001-02-04 23:20:18 +1100169local_do_ls(const char *args)
170{
171 if (!args || !*args)
Damien Millerd7686fd2001-02-10 00:40:03 +1100172 local_do_shell(_PATH_LS);
Damien Miller33804262001-02-04 23:20:18 +1100173 else {
Damien Millerd7686fd2001-02-10 00:40:03 +1100174 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
175 char *buf = xmalloc(len);
Damien Miller33804262001-02-04 23:20:18 +1100176
177 /* XXX: quoting - rip quoting code from ftp? */
Damien Millerd7686fd2001-02-10 00:40:03 +1100178 snprintf(buf, len, _PATH_LS " %s", args);
Damien Miller33804262001-02-04 23:20:18 +1100179 local_do_shell(buf);
Damien Millerd7686fd2001-02-10 00:40:03 +1100180 xfree(buf);
Damien Miller33804262001-02-04 23:20:18 +1100181 }
182}
183
184char *
185make_absolute(char *p, char *pwd)
186{
187 char buf[2048];
188
189 /* Derelativise */
190 if (p && p[0] != '/') {
191 snprintf(buf, sizeof(buf), "%s/%s", pwd, p);
192 xfree(p);
193 p = xstrdup(buf);
194 }
195
196 return(p);
197}
198
199int
200parse_getput_flags(const char **cpp, int *pflag)
201{
202 const char *cp = *cpp;
203
204 /* Check for flags */
205 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100206 switch (cp[1]) {
Damien Miller33804262001-02-04 23:20:18 +1100207 case 'P':
208 *pflag = 1;
209 break;
210 default:
211 error("Invalid flag -%c", *cp);
212 return(-1);
213 }
214 cp += 2;
215 *cpp = cp + strspn(cp, WHITESPACE);
216 }
217
218 return(0);
219}
220
221int
222get_pathname(const char **cpp, char **path)
223{
Damien Millerd7686fd2001-02-10 00:40:03 +1100224 const char *cp = *cpp, *end;
225 char quot;
Damien Miller33804262001-02-04 23:20:18 +1100226 int i;
227
228 cp += strspn(cp, WHITESPACE);
229 if (!*cp) {
230 *cpp = cp;
231 *path = NULL;
Damien Millerd7686fd2001-02-10 00:40:03 +1100232 return (0);
Damien Miller33804262001-02-04 23:20:18 +1100233 }
234
235 /* Check for quoted filenames */
236 if (*cp == '\"' || *cp == '\'') {
Damien Millerd7686fd2001-02-10 00:40:03 +1100237 quot = *cp++;
238
239 end = strchr(cp, quot);
240 if (end == NULL) {
Damien Miller33804262001-02-04 23:20:18 +1100241 error("Unterminated quote");
Damien Millerd7686fd2001-02-10 00:40:03 +1100242 goto fail;
Damien Miller33804262001-02-04 23:20:18 +1100243 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100244 if (cp == end) {
Damien Miller33804262001-02-04 23:20:18 +1100245 error("Empty quotes");
Damien Millerd7686fd2001-02-10 00:40:03 +1100246 goto fail;
Damien Miller33804262001-02-04 23:20:18 +1100247 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100248 *cpp = end + 1 + strspn(end + 1, WHITESPACE);
249 } else {
250 /* Read to end of filename */
251 end = strpbrk(cp, WHITESPACE);
252 if (end == NULL)
253 end = strchr(cp, '\0');
254 *cpp = end + strspn(end, WHITESPACE);
Damien Miller33804262001-02-04 23:20:18 +1100255 }
256
Damien Millerd7686fd2001-02-10 00:40:03 +1100257 i = end - cp;
Damien Miller33804262001-02-04 23:20:18 +1100258
259 *path = xmalloc(i + 1);
260 memcpy(*path, cp, i);
261 (*path)[i] = '\0';
Damien Miller33804262001-02-04 23:20:18 +1100262 return(0);
Damien Millerd7686fd2001-02-10 00:40:03 +1100263
264 fail:
265 *path = NULL;
266 return (-1);
Damien Miller33804262001-02-04 23:20:18 +1100267}
268
269int
270infer_path(const char *p, char **ifp)
271{
272 char *cp;
273
274 debug("XXX: P = \"%s\"", p);
275
276 cp = strrchr(p, '/');
Damien Miller33804262001-02-04 23:20:18 +1100277 if (cp == NULL) {
278 *ifp = xstrdup(p);
279 return(0);
280 }
281
282 if (!cp[1]) {
283 error("Invalid path");
284 return(-1);
285 }
286
287 *ifp = xstrdup(cp + 1);
288 return(0);
289}
290
291int
292parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
293 char **path1, char **path2)
294{
295 const char *cmd, *cp = *cpp;
Ben Lindstrom66904942001-02-15 03:19:56 +0000296 char *cp2;
Kevin Steves62c45db2001-02-05 13:42:43 +0000297 int base = 0;
Ben Lindstrom66904942001-02-15 03:19:56 +0000298 long l;
Damien Miller33804262001-02-04 23:20:18 +1100299 int i, cmdnum;
300
301 /* Skip leading whitespace */
302 cp = cp + strspn(cp, WHITESPACE);
303
304 /* Ignore blank lines */
305 if (!*cp)
306 return(-1);
307
308 /* Figure out which command we have */
309 for(i = 0; cmds[i].c; i++) {
310 int cmdlen = strlen(cmds[i].c);
311
312 /* Check for command followed by whitespace */
313 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
314 strchr(WHITESPACE, cp[cmdlen])) {
315 cp += cmdlen;
316 cp = cp + strspn(cp, WHITESPACE);
317 break;
318 }
319 }
320 cmdnum = cmds[i].n;
321 cmd = cmds[i].c;
322
323 /* Special case */
324 if (*cp == '!') {
325 cp++;
326 cmdnum = I_SHELL;
327 } else if (cmdnum == -1) {
328 error("Invalid command.");
329 return(-1);
330 }
331
332 /* Get arguments and parse flags */
333 *pflag = *n_arg = 0;
334 *path1 = *path2 = NULL;
335 switch (cmdnum) {
336 case I_GET:
337 case I_PUT:
338 if (parse_getput_flags(&cp, pflag))
339 return(-1);
340 /* Get first pathname (mandatory) */
341 if (get_pathname(&cp, path1))
342 return(-1);
343 if (*path1 == NULL) {
344 error("You must specify at least one path after a "
345 "%s command.", cmd);
346 return(-1);
347 }
348 /* Try to get second pathname (optional) */
349 if (get_pathname(&cp, path2))
350 return(-1);
351 /* Otherwise try to guess it from first path */
352 if (*path2 == NULL && infer_path(*path1, path2))
353 return(-1);
354 break;
355 case I_RENAME:
356 /* Get first pathname (mandatory) */
357 if (get_pathname(&cp, path1))
358 return(-1);
359 if (get_pathname(&cp, path2))
360 return(-1);
361 if (!*path1 || !*path2) {
362 error("You must specify two paths after a %s "
363 "command.", cmd);
364 return(-1);
365 }
366 break;
367 case I_RM:
368 case I_MKDIR:
369 case I_RMDIR:
370 case I_CHDIR:
371 case I_LCHDIR:
372 case I_LMKDIR:
373 /* Get pathname (mandatory) */
374 if (get_pathname(&cp, path1))
375 return(-1);
376 if (*path1 == NULL) {
Kevin Stevesef4eea92001-02-05 12:42:17 +0000377 error("You must specify a path after a %s command.",
Damien Miller33804262001-02-04 23:20:18 +1100378 cmd);
379 return(-1);
380 }
381 break;
382 case I_LS:
383 /* Path is optional */
384 if (get_pathname(&cp, path1))
385 return(-1);
386 break;
387 case I_LLS:
388 case I_SHELL:
389 /* Uses the rest of the line */
390 break;
391 case I_LUMASK:
Ben Lindstrom66904942001-02-15 03:19:56 +0000392 base = 8;
Damien Miller33804262001-02-04 23:20:18 +1100393 case I_CHMOD:
Kevin Steves62c45db2001-02-05 13:42:43 +0000394 base = 8;
Damien Miller33804262001-02-04 23:20:18 +1100395 case I_CHOWN:
396 case I_CHGRP:
397 /* Get numeric arg (mandatory) */
Ben Lindstrom66904942001-02-15 03:19:56 +0000398 l = strtol(cp, &cp2, base);
399 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
400 errno == ERANGE) || l < 0) {
Damien Miller33804262001-02-04 23:20:18 +1100401 error("You must supply a numeric argument "
402 "to the %s command.", cmd);
403 return(-1);
404 }
Ben Lindstrom66904942001-02-15 03:19:56 +0000405 cp = cp2;
406 *n_arg = l;
407 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
408 break;
409 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
Damien Miller33804262001-02-04 23:20:18 +1100410 error("You must supply a numeric argument "
411 "to the %s command.", cmd);
412 return(-1);
413 }
414 cp += strspn(cp, WHITESPACE);
415
416 /* Get pathname (mandatory) */
417 if (get_pathname(&cp, path1))
418 return(-1);
419 if (*path1 == NULL) {
Kevin Stevesef4eea92001-02-05 12:42:17 +0000420 error("You must specify a path after a %s command.",
Damien Miller33804262001-02-04 23:20:18 +1100421 cmd);
422 return(-1);
423 }
424 break;
425 case I_QUIT:
426 case I_PWD:
427 case I_LPWD:
428 case I_HELP:
429 break;
430 default:
431 fatal("Command not implemented");
432 }
433
434 *cpp = cp;
Damien Miller33804262001-02-04 23:20:18 +1100435 return(cmdnum);
436}
437
438int
439parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
440{
Damien Millerd7686fd2001-02-10 00:40:03 +1100441 char *path1, *path2, *tmp;
Damien Miller33804262001-02-04 23:20:18 +1100442 int pflag, cmdnum;
443 unsigned long n_arg;
444 Attrib a, *aa;
445 char path_buf[PATH_MAX];
446
447 path1 = path2 = NULL;
448 cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);
449
450 /* Perform command */
451 switch (cmdnum) {
452 case -1:
453 break;
454 case I_GET:
455 path1 = make_absolute(path1, *pwd);
456 do_download(in, out, path1, path2, pflag);
457 break;
458 case I_PUT:
459 path2 = make_absolute(path2, *pwd);
460 do_upload(in, out, path1, path2, pflag);
461 break;
462 case I_RENAME:
463 path1 = make_absolute(path1, *pwd);
464 path2 = make_absolute(path2, *pwd);
465 do_rename(in, out, path1, path2);
466 break;
467 case I_RM:
468 path1 = make_absolute(path1, *pwd);
469 do_rm(in, out, path1);
470 break;
471 case I_MKDIR:
472 path1 = make_absolute(path1, *pwd);
473 attrib_clear(&a);
474 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
475 a.perm = 0777;
476 do_mkdir(in, out, path1, &a);
477 break;
478 case I_RMDIR:
479 path1 = make_absolute(path1, *pwd);
480 do_rmdir(in, out, path1);
481 break;
482 case I_CHDIR:
483 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100484 if ((tmp = do_realpath(in, out, path1)) == NULL)
485 break;
486 if ((aa = do_stat(in, out, tmp)) == NULL) {
487 xfree(tmp);
488 break;
489 }
490 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
491 error("Can't change directory: Can't check target");
492 xfree(tmp);
493 break;
494 }
495 if (!S_ISDIR(aa->perm)) {
496 error("Can't change directory: \"%s\" is not "
497 "a directory", tmp);
498 xfree(tmp);
499 break;
500 }
Damien Miller33804262001-02-04 23:20:18 +1100501 xfree(*pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100502 *pwd = tmp;
Damien Miller33804262001-02-04 23:20:18 +1100503 break;
504 case I_LS:
Damien Millerd7686fd2001-02-10 00:40:03 +1100505 if (!path1) {
506 do_ls(in, out, *pwd);
507 break;
508 }
Damien Miller33804262001-02-04 23:20:18 +1100509 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100510 if ((tmp = do_realpath(in, out, path1)) == NULL)
511 break;
512 xfree(path1);
513 path1 = tmp;
514 if ((aa = do_stat(in, out, path1)) == NULL)
515 break;
516 if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
517 !S_ISDIR(aa->perm)) {
518 error("Can't ls: \"%s\" is not a directory", path1);
519 break;
520 }
521 do_ls(in, out, path1);
Damien Miller33804262001-02-04 23:20:18 +1100522 break;
523 case I_LCHDIR:
524 if (chdir(path1) == -1)
525 error("Couldn't change local directory to "
526 "\"%s\": %s", path1, strerror(errno));
527 break;
528 case I_LMKDIR:
529 if (mkdir(path1, 0777) == -1)
Damien Millerd7686fd2001-02-10 00:40:03 +1100530 error("Couldn't create local directory "
Damien Miller33804262001-02-04 23:20:18 +1100531 "\"%s\": %s", path1, strerror(errno));
532 break;
533 case I_LLS:
534 local_do_ls(cmd);
535 break;
536 case I_SHELL:
537 local_do_shell(cmd);
538 break;
539 case I_LUMASK:
540 umask(n_arg);
Ben Lindstrom66904942001-02-15 03:19:56 +0000541 printf("Local umask: %03lo\n", n_arg);
Damien Miller33804262001-02-04 23:20:18 +1100542 break;
543 case I_CHMOD:
544 path1 = make_absolute(path1, *pwd);
545 attrib_clear(&a);
546 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
547 a.perm = n_arg;
548 do_setstat(in, out, path1, &a);
Kevin Steves62c45db2001-02-05 13:42:43 +0000549 break;
Damien Miller33804262001-02-04 23:20:18 +1100550 case I_CHOWN:
551 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100552 if (!(aa = do_stat(in, out, path1)))
553 break;
Kevin Steves62c45db2001-02-05 13:42:43 +0000554 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
Damien Miller33804262001-02-04 23:20:18 +1100555 error("Can't get current ownership of "
556 "remote file \"%s\"", path1);
557 break;
558 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100559 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
Damien Miller33804262001-02-04 23:20:18 +1100560 aa->uid = n_arg;
561 do_setstat(in, out, path1, aa);
562 break;
563 case I_CHGRP:
564 path1 = make_absolute(path1, *pwd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100565 if (!(aa = do_stat(in, out, path1)))
566 break;
Kevin Steves62c45db2001-02-05 13:42:43 +0000567 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
Damien Miller33804262001-02-04 23:20:18 +1100568 error("Can't get current ownership of "
569 "remote file \"%s\"", path1);
570 break;
571 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100572 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
Damien Miller33804262001-02-04 23:20:18 +1100573 aa->gid = n_arg;
574 do_setstat(in, out, path1, aa);
575 break;
576 case I_PWD:
577 printf("Remote working directory: %s\n", *pwd);
578 break;
579 case I_LPWD:
580 if (!getcwd(path_buf, sizeof(path_buf)))
581 error("Couldn't get local cwd: %s\n",
582 strerror(errno));
583 else
584 printf("Local working directory: %s\n",
585 path_buf);
586 break;
587 case I_QUIT:
588 return(-1);
589 case I_HELP:
590 help();
591 break;
592 default:
593 fatal("%d is not implemented", cmdnum);
594 }
595
596 if (path1)
597 xfree(path1);
598 if (path2)
599 xfree(path2);
Damien Miller33804262001-02-04 23:20:18 +1100600 return(0);
601}
602
603void
604interactive_loop(int fd_in, int fd_out)
605{
606 char *pwd;
607 char cmd[2048];
608
609 pwd = do_realpath(fd_in, fd_out, ".");
610 if (pwd == NULL)
611 fatal("Need cwd");
612
Damien Millerd7686fd2001-02-10 00:40:03 +1100613 setvbuf(stdout, NULL, _IOLBF, 0);
614 setvbuf(stdin, NULL, _IOLBF, 0);
Damien Miller33804262001-02-04 23:20:18 +1100615
616 for(;;) {
617 char *cp;
618
619 printf("sftp> ");
620
621 /* XXX: use libedit */
622 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
623 printf("\n");
624 break;
625 }
626 cp = strrchr(cmd, '\n');
627 if (cp)
628 *cp = '\0';
629 if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))
630 break;
631 }
632 xfree(pwd);
633}