4445b8b7e38286ecb2c05b2f10e39b8c22f6408d
2 * psftp.c: front end for PSFTP.
13 #define smalloc malloc
14 #define srealloc realloc
17 /* ----------------------------------------------------------------------
18 * String handling routines.
21 char *dupstr(char *s
) {
23 char *p
= smalloc(len
+1);
28 /* ----------------------------------------------------------------------
34 /* ----------------------------------------------------------------------
35 * Higher-level helper functions used in commands.
39 * Canonify a pathname starting from the pwd.
41 char *canonify(char *name
) {
43 return fxp_realpath(name
, NULL
);
45 return fxp_realpath(pwd
, name
);
48 /* ----------------------------------------------------------------------
49 * Actual sftp commands.
53 int nwords
, wordssize
;
54 int (*obey
)(struct sftp_command
*);/* returns <0 to quit */
57 int sftp_cmd_null(struct sftp_command
*cmd
) {
61 int sftp_cmd_unknown(struct sftp_command
*cmd
) {
62 printf("psftp: unknown command \"%s\"\n", cmd
->words
[0]);
66 int sftp_cmd_quit(struct sftp_command
*cmd
) {
71 * List a directory. If no arguments are given, list pwd; otherwise
72 * list the directory given in words[1].
74 static int sftp_ls_compare(const void *av
, const void *bv
) {
75 const struct fxp_name
*a
= (const struct fxp_name
*)av
;
76 const struct fxp_name
*b
= (const struct fxp_name
*)bv
;
77 return strcmp(a
->filename
, b
->filename
);
79 int sftp_cmd_ls(struct sftp_command
*cmd
) {
80 struct fxp_handle
*dirh
;
81 struct fxp_names
*names
;
82 struct fxp_name
*ournames
;
94 printf("%s: %s\n", dir
, fxp_error());
98 printf("Listing directory %s\n", cdir
);
100 dirh
= fxp_opendir(cdir
);
102 printf("Unable to open %s: %s\n", dir
, fxp_error());
104 nnames
= namesize
= 0;
109 names
= fxp_readdir(dirh
);
111 if (fxp_error_type() == SSH_FX_EOF
)
113 printf("Reading directory %s: %s\n", dir
, fxp_error());
116 if (names
->nnames
== 0) {
117 fxp_free_names(names
);
121 if (nnames
+ names
->nnames
>= namesize
) {
122 namesize
+= names
->nnames
+ 128;
123 ournames
= srealloc(ournames
, namesize
* sizeof(*ournames
));
126 for (i
= 0; i
< names
->nnames
; i
++)
127 ournames
[nnames
++] = names
->names
[i
];
129 names
->nnames
= 0; /* prevent free_names */
130 fxp_free_names(names
);
135 * Now we have our filenames. Sort them by actual file
136 * name, and then output the longname parts.
138 qsort(ournames
, nnames
, sizeof(*ournames
), sftp_ls_compare
);
143 for (i
= 0; i
< nnames
; i
++)
144 printf("%s\n", ournames
[i
].longname
);
153 * Change directories. We do this by canonifying the new name, then
154 * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
156 int sftp_cmd_cd(struct sftp_command
*cmd
) {
157 struct fxp_handle
*dirh
;
161 dir
= fxp_realpath(".", NULL
);
163 dir
= canonify(cmd
->words
[1]);
166 printf("%s: %s\n", dir
, fxp_error());
170 dirh
= fxp_opendir(dir
);
172 printf("Directory %s: %s\n", dir
, fxp_error());
181 printf("Remote directory is now %s\n", pwd
);
187 * Get a file and save it at the local end.
189 int sftp_cmd_get(struct sftp_command
*cmd
) {
190 struct fxp_handle
*fh
;
191 char *fname
, *outfname
;
195 if (cmd
->nwords
< 2) {
196 printf("get: expects a filename\n");
200 fname
= canonify(cmd
->words
[1]);
202 printf("%s: %s\n", cmd
->words
[1], fxp_error());
205 outfname
= (cmd
->nwords
== 2 ? cmd
->words
[1] : cmd
->words
[2]);
207 fh
= fxp_open(fname
, SSH_FXF_READ
);
209 printf("%s: %s\n", fname
, fxp_error());
213 fp
= fopen(outfname
, "wb");
215 printf("local: unable to open %s\n", outfname
);
221 printf("remote:%s => local:%s\n", fname
, outfname
);
223 offset
= uint64_make(0,0);
226 * FIXME: we can use FXP_FSTAT here to get the file size, and
227 * thus put up a progress bar.
234 len
= fxp_read(fh
, buffer
, offset
, sizeof(buffer
));
235 if ((len
== -1 && fxp_error_type() == SSH_FX_EOF
) ||
239 printf("error while reading: %s\n", fxp_error());
245 wlen
= fwrite(buffer
, 1, len
-wpos
, fp
);
247 printf("error while writing local file\n");
252 if (wpos
< len
) /* we had an error */
254 offset
= uint64_add32(offset
, len
);
265 * Send a file and store it at the remote end.
267 int sftp_cmd_put(struct sftp_command
*cmd
) {
268 struct fxp_handle
*fh
;
269 char *fname
, *origoutfname
, *outfname
;
273 if (cmd
->nwords
< 2) {
274 printf("put: expects a filename\n");
278 fname
= cmd
->words
[1];
279 origoutfname
= (cmd
->nwords
== 2 ? cmd
->words
[1] : cmd
->words
[2]);
280 outfname
= canonify(origoutfname
);
282 printf("%s: %s\n", origoutfname
, fxp_error());
286 fp
= fopen(fname
, "rb");
288 printf("local: unable to open %s\n", fname
);
293 fh
= fxp_open(outfname
, SSH_FXF_WRITE
| SSH_FXF_CREAT
| SSH_FXF_TRUNC
);
295 printf("%s: %s\n", outfname
, fxp_error());
300 printf("local:%s => remote:%s\n", fname
, outfname
);
302 offset
= uint64_make(0,0);
305 * FIXME: we can use FXP_FSTAT here to get the file size, and
306 * thus put up a progress bar.
312 len
= fread(buffer
, 1, len
, fp
);
314 printf("error while reading local file\n");
316 } else if (len
== 0) {
319 if (!fxp_write(fh
, buffer
, offset
, sizeof(buffer
))) {
320 printf("error while writing: %s\n", fxp_error());
323 offset
= uint64_add32(offset
, len
);
333 static struct sftp_cmd_lookup
{
335 int (*obey
)(struct sftp_command
*);
338 * List of sftp commands. This is binary-searched so it MUST be
341 {"bye", sftp_cmd_quit
},
343 {"exit", sftp_cmd_quit
},
344 {"get", sftp_cmd_get
},
346 {"put", sftp_cmd_put
},
347 {"quit", sftp_cmd_quit
},
350 /* ----------------------------------------------------------------------
351 * Command line reading and parsing.
353 struct sftp_command
*sftp_getcmd(void) {
355 int linelen
, linesize
;
356 struct sftp_command
*cmd
;
363 cmd
= smalloc(sizeof(struct sftp_command
));
369 linesize
= linelen
= 0;
375 line
= srealloc(line
, linesize
);
376 ret
= fgets(line
+linelen
, linesize
-linelen
, stdin
);
378 if (!ret
|| (linelen
== 0 && line
[0] == '\0')) {
379 cmd
->obey
= sftp_cmd_quit
;
381 return cmd
; /* eof */
383 len
= linelen
+ strlen(line
+linelen
);
385 if (line
[linelen
-1] == '\n') {
387 line
[linelen
] = '\0';
393 * Parse the command line into words. The syntax is:
394 * - double quotes are removed, but cause spaces within to be
395 * treated as non-separating.
396 * - a double-doublequote pair is a literal double quote, inside
397 * _or_ outside quotes. Like this:
399 * firstword "second word" "this has ""quotes"" in" sodoes""this""
405 * >this has "quotes" in<
410 /* skip whitespace */
411 while (*p
&& (*p
== ' ' || *p
== '\t')) p
++;
412 /* mark start of word */
413 q
= r
= p
; /* q sits at start, r writes word */
416 if (!quoting
&& (*p
== ' ' || *p
== '\t'))
417 break; /* reached end of word */
418 else if (*p
== '"' && p
[1] == '"')
419 p
+=2, *r
++ = '"'; /* a literal quote */
421 p
++, quoting
= !quoting
;
425 if (*p
) p
++; /* skip over the whitespace */
427 if (cmd
->nwords
>= cmd
->wordssize
) {
428 cmd
->wordssize
= cmd
->nwords
+ 16;
429 cmd
->words
= srealloc(cmd
->words
, cmd
->wordssize
*sizeof(char *));
431 cmd
->words
[cmd
->nwords
++] = q
;
435 * Now parse the first word and assign a function.
438 if (cmd
->nwords
== 0)
439 cmd
->obey
= sftp_cmd_null
;
443 cmd
->obey
= sftp_cmd_unknown
;
446 j
= sizeof(sftp_lookup
) / sizeof(*sftp_lookup
);
449 cmp
= strcmp(cmd
->words
[0], sftp_lookup
[k
].name
);
455 cmd
->obey
= sftp_lookup
[k
].obey
;
466 * Do protocol initialisation.
470 "Fatal: unable to initialise SFTP: %s\n",
475 * Find out where our home directory is.
477 homedir
= fxp_realpath(".", NULL
);
480 "Warning: failed to resolve home directory: %s\n",
482 homedir
= dupstr(".");
484 printf("Remote working directory is %s\n", homedir
);
486 pwd
= dupstr(homedir
);
488 /* ------------------------------------------------------------------
489 * Now we're ready to do Real Stuff.
492 struct sftp_command
*cmd
;
496 if (cmd
->obey(cmd
) < 0)
500 /* ------------------------------------------------------------------
501 * We've received an exit command. Tidy up and leave.