50be458227d98296b1816ddad2768d5fe1dcf7d5
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 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */
29 char *dupcat(char *s1
, ...) {
37 sn
= va_arg(ap
, char *);
50 sn
= va_arg(ap
, char *);
61 /* ----------------------------------------------------------------------
67 /* ----------------------------------------------------------------------
68 * Higher-level helper functions used in commands.
72 * Attempt to canonify a pathname starting from the pwd. If
73 * canonification fails, at least fall back to returning a _valid_
74 * pathname (though it may be ugly, eg /home/simon/../foobar).
76 char *canonify(char *name
) {
77 char *fullname
, *canonname
;
79 fullname
= dupstr(name
);
81 fullname
= dupcat(pwd
, "/", name
, NULL
);
83 canonname
= fxp_realpath(name
);
91 /* ----------------------------------------------------------------------
92 * Actual sftp commands.
96 int nwords
, wordssize
;
97 int (*obey
)(struct sftp_command
*);/* returns <0 to quit */
100 int sftp_cmd_null(struct sftp_command
*cmd
) {
104 int sftp_cmd_unknown(struct sftp_command
*cmd
) {
105 printf("psftp: unknown command \"%s\"\n", cmd
->words
[0]);
109 int sftp_cmd_quit(struct sftp_command
*cmd
) {
114 * List a directory. If no arguments are given, list pwd; otherwise
115 * list the directory given in words[1].
117 static int sftp_ls_compare(const void *av
, const void *bv
) {
118 const struct fxp_name
*a
= (const struct fxp_name
*)av
;
119 const struct fxp_name
*b
= (const struct fxp_name
*)bv
;
120 return strcmp(a
->filename
, b
->filename
);
122 int sftp_cmd_ls(struct sftp_command
*cmd
) {
123 struct fxp_handle
*dirh
;
124 struct fxp_names
*names
;
125 struct fxp_name
*ournames
;
126 int nnames
, namesize
;
135 cdir
= canonify(dir
);
137 printf("%s: %s\n", dir
, fxp_error());
141 printf("Listing directory %s\n", cdir
);
143 dirh
= fxp_opendir(cdir
);
145 printf("Unable to open %s: %s\n", dir
, fxp_error());
147 nnames
= namesize
= 0;
152 names
= fxp_readdir(dirh
);
154 if (fxp_error_type() == SSH_FX_EOF
)
156 printf("Reading directory %s: %s\n", dir
, fxp_error());
159 if (names
->nnames
== 0) {
160 fxp_free_names(names
);
164 if (nnames
+ names
->nnames
>= namesize
) {
165 namesize
+= names
->nnames
+ 128;
166 ournames
= srealloc(ournames
, namesize
* sizeof(*ournames
));
169 for (i
= 0; i
< names
->nnames
; i
++)
170 ournames
[nnames
++] = names
->names
[i
];
172 names
->nnames
= 0; /* prevent free_names */
173 fxp_free_names(names
);
178 * Now we have our filenames. Sort them by actual file
179 * name, and then output the longname parts.
181 qsort(ournames
, nnames
, sizeof(*ournames
), sftp_ls_compare
);
186 for (i
= 0; i
< nnames
; i
++)
187 printf("%s\n", ournames
[i
].longname
);
196 * Change directories. We do this by canonifying the new name, then
197 * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
199 int sftp_cmd_cd(struct sftp_command
*cmd
) {
200 struct fxp_handle
*dirh
;
204 dir
= dupstr(homedir
);
206 dir
= canonify(cmd
->words
[1]);
209 printf("%s: %s\n", dir
, fxp_error());
213 dirh
= fxp_opendir(dir
);
215 printf("Directory %s: %s\n", dir
, fxp_error());
224 printf("Remote directory is now %s\n", pwd
);
230 * Get a file and save it at the local end.
232 int sftp_cmd_get(struct sftp_command
*cmd
) {
233 struct fxp_handle
*fh
;
234 char *fname
, *outfname
;
238 if (cmd
->nwords
< 2) {
239 printf("get: expects a filename\n");
243 fname
= canonify(cmd
->words
[1]);
245 printf("%s: %s\n", cmd
->words
[1], fxp_error());
248 outfname
= (cmd
->nwords
== 2 ? cmd
->words
[1] : cmd
->words
[2]);
250 fh
= fxp_open(fname
, SSH_FXF_READ
);
252 printf("%s: %s\n", fname
, fxp_error());
256 fp
= fopen(outfname
, "wb");
258 printf("local: unable to open %s\n", outfname
);
264 printf("remote:%s => local:%s\n", fname
, outfname
);
266 offset
= uint64_make(0,0);
269 * FIXME: we can use FXP_FSTAT here to get the file size, and
270 * thus put up a progress bar.
277 len
= fxp_read(fh
, buffer
, offset
, sizeof(buffer
));
278 if ((len
== -1 && fxp_error_type() == SSH_FX_EOF
) ||
282 printf("error while reading: %s\n", fxp_error());
288 wlen
= fwrite(buffer
, 1, len
-wpos
, fp
);
290 printf("error while writing local file\n");
295 if (wpos
< len
) /* we had an error */
297 offset
= uint64_add32(offset
, len
);
308 * Send a file and store it at the remote end.
310 int sftp_cmd_put(struct sftp_command
*cmd
) {
311 struct fxp_handle
*fh
;
312 char *fname
, *origoutfname
, *outfname
;
316 if (cmd
->nwords
< 2) {
317 printf("put: expects a filename\n");
321 fname
= cmd
->words
[1];
322 origoutfname
= (cmd
->nwords
== 2 ? cmd
->words
[1] : cmd
->words
[2]);
323 outfname
= canonify(origoutfname
);
325 printf("%s: %s\n", origoutfname
, fxp_error());
329 fp
= fopen(fname
, "rb");
331 printf("local: unable to open %s\n", fname
);
336 fh
= fxp_open(outfname
, SSH_FXF_WRITE
| SSH_FXF_CREAT
| SSH_FXF_TRUNC
);
338 printf("%s: %s\n", outfname
, fxp_error());
343 printf("local:%s => remote:%s\n", fname
, outfname
);
345 offset
= uint64_make(0,0);
348 * FIXME: we can use FXP_FSTAT here to get the file size, and
349 * thus put up a progress bar.
355 len
= fread(buffer
, 1, sizeof(buffer
), fp
);
357 printf("error while reading local file\n");
359 } else if (len
== 0) {
362 if (!fxp_write(fh
, buffer
, offset
, len
)) {
363 printf("error while writing: %s\n", fxp_error());
366 offset
= uint64_add32(offset
, len
);
376 static struct sftp_cmd_lookup
{
378 int (*obey
)(struct sftp_command
*);
381 * List of sftp commands. This is binary-searched so it MUST be
384 {"bye", sftp_cmd_quit
},
386 {"exit", sftp_cmd_quit
},
387 {"get", sftp_cmd_get
},
389 {"put", sftp_cmd_put
},
390 {"quit", sftp_cmd_quit
},
393 /* ----------------------------------------------------------------------
394 * Command line reading and parsing.
396 struct sftp_command
*sftp_getcmd(void) {
398 int linelen
, linesize
;
399 struct sftp_command
*cmd
;
406 cmd
= smalloc(sizeof(struct sftp_command
));
412 linesize
= linelen
= 0;
418 line
= srealloc(line
, linesize
);
419 ret
= fgets(line
+linelen
, linesize
-linelen
, stdin
);
421 if (!ret
|| (linelen
== 0 && line
[0] == '\0')) {
422 cmd
->obey
= sftp_cmd_quit
;
424 return cmd
; /* eof */
426 len
= linelen
+ strlen(line
+linelen
);
428 if (line
[linelen
-1] == '\n') {
430 line
[linelen
] = '\0';
436 * Parse the command line into words. The syntax is:
437 * - double quotes are removed, but cause spaces within to be
438 * treated as non-separating.
439 * - a double-doublequote pair is a literal double quote, inside
440 * _or_ outside quotes. Like this:
442 * firstword "second word" "this has ""quotes"" in" sodoes""this""
448 * >this has "quotes" in<
453 /* skip whitespace */
454 while (*p
&& (*p
== ' ' || *p
== '\t')) p
++;
455 /* mark start of word */
456 q
= r
= p
; /* q sits at start, r writes word */
459 if (!quoting
&& (*p
== ' ' || *p
== '\t'))
460 break; /* reached end of word */
461 else if (*p
== '"' && p
[1] == '"')
462 p
+=2, *r
++ = '"'; /* a literal quote */
464 p
++, quoting
= !quoting
;
468 if (*p
) p
++; /* skip over the whitespace */
470 if (cmd
->nwords
>= cmd
->wordssize
) {
471 cmd
->wordssize
= cmd
->nwords
+ 16;
472 cmd
->words
= srealloc(cmd
->words
, cmd
->wordssize
*sizeof(char *));
474 cmd
->words
[cmd
->nwords
++] = q
;
478 * Now parse the first word and assign a function.
481 if (cmd
->nwords
== 0)
482 cmd
->obey
= sftp_cmd_null
;
486 cmd
->obey
= sftp_cmd_unknown
;
489 j
= sizeof(sftp_lookup
) / sizeof(*sftp_lookup
);
492 cmp
= strcmp(cmd
->words
[0], sftp_lookup
[k
].name
);
498 cmd
->obey
= sftp_lookup
[k
].obey
;
509 * Do protocol initialisation.
513 "Fatal: unable to initialise SFTP: %s\n",
518 * Find out where our home directory is.
520 homedir
= fxp_realpath(".");
523 "Warning: failed to resolve home directory: %s\n",
525 homedir
= dupstr(".");
527 printf("Remote working directory is %s\n", homedir
);
529 pwd
= dupstr(homedir
);
531 /* ------------------------------------------------------------------
532 * Now we're ready to do Real Stuff.
535 struct sftp_command
*cmd
;
539 if (cmd
->obey(cmd
) < 0)
543 /* ------------------------------------------------------------------
544 * We've received an exit command. Tidy up and leave.