+ cleanup:
+ req = fxp_close_send(fh);
+ pktin = sftp_wait_for_reply(req);
+ ret = fxp_close_recv(pktin, req);
+ if (!ret) {
+ if (!err) {
+ printf("error while closing: %s", fxp_error());
+ err = 1;
+ }
+ }
+
+ close_rfile(file);
+
+ return (err == 0) ? 1 : 0;
+}
+
+/* ----------------------------------------------------------------------
+ * A remote wildcard matcher, providing a similar interface to the
+ * local one in psftp.h.
+ */
+
+typedef struct SftpWildcardMatcher {
+ struct fxp_handle *dirh;
+ struct fxp_names *names;
+ int namepos;
+ char *wildcard, *prefix;
+} SftpWildcardMatcher;
+
+SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)
+{
+ struct sftp_packet *pktin;
+ struct sftp_request *req;
+ char *wildcard;
+ char *unwcdir, *tmpdir, *cdir;
+ int len, check;
+ SftpWildcardMatcher *swcm;
+ struct fxp_handle *dirh;
+
+ /*
+ * We don't handle multi-level wildcards; so we expect to find
+ * a fully specified directory part, followed by a wildcard
+ * after that.
+ */
+ wildcard = stripslashes(name, 0);
+
+ unwcdir = dupstr(name);
+ len = wildcard - name;
+ unwcdir[len] = '\0';
+ if (len > 0 && unwcdir[len-1] == '/')
+ unwcdir[len-1] = '\0';
+ tmpdir = snewn(1 + len, char);
+ check = wc_unescape(tmpdir, unwcdir);
+ sfree(tmpdir);
+
+ if (!check) {
+ printf("Multiple-level wildcards are not supported\n");
+ sfree(unwcdir);
+ return NULL;
+ }
+
+ cdir = canonify(unwcdir);
+
+ req = fxp_opendir_send(cdir);
+ pktin = sftp_wait_for_reply(req);
+ dirh = fxp_opendir_recv(pktin, req);
+
+ if (dirh) {
+ swcm = snew(SftpWildcardMatcher);
+ swcm->dirh = dirh;
+ swcm->names = NULL;
+ swcm->wildcard = dupstr(wildcard);
+ swcm->prefix = unwcdir;
+ } else {
+ printf("Unable to open %s: %s\n", cdir, fxp_error());
+ swcm = NULL;
+ sfree(unwcdir);
+ }
+
+ sfree(cdir);
+
+ return swcm;
+}
+
+char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
+{
+ struct fxp_name *name;
+ struct sftp_packet *pktin;
+ struct sftp_request *req;
+
+ while (1) {
+ if (swcm->names && swcm->namepos >= swcm->names->nnames) {
+ fxp_free_names(swcm->names);
+ swcm->names = NULL;
+ }
+
+ if (!swcm->names) {
+ req = fxp_readdir_send(swcm->dirh);
+ pktin = sftp_wait_for_reply(req);
+ swcm->names = fxp_readdir_recv(pktin, req);
+
+ if (!swcm->names) {
+ if (fxp_error_type() != SSH_FX_EOF)
+ printf("%s: reading directory: %s\n", swcm->prefix,
+ fxp_error());
+ return NULL;
+ } else if (swcm->names->nnames == 0) {
+ /*
+ * Another failure mode which we treat as EOF is if
+ * the server reports success from FXP_READDIR but
+ * returns no actual names. This is unusual, since
+ * from most servers you'd expect at least "." and
+ * "..", but there's nothing forbidding a server from
+ * omitting those if it wants to.
+ */
+ return NULL;
+ }
+
+ swcm->namepos = 0;
+ }
+
+ assert(swcm->names && swcm->namepos < swcm->names->nnames);
+
+ name = &swcm->names->names[swcm->namepos++];
+
+ if (!strcmp(name->filename, ".") || !strcmp(name->filename, ".."))
+ continue; /* expected bad filenames */
+
+ if (!vet_filename(name->filename)) {
+ printf("ignoring potentially dangerous server-"
+ "supplied filename '%s'\n", name->filename);
+ continue; /* unexpected bad filename */
+ }
+
+ if (!wc_match(swcm->wildcard, name->filename))
+ continue; /* doesn't match the wildcard */
+
+ /*
+ * We have a working filename. Return it.
+ */
+ return dupprintf("%s%s%s", swcm->prefix,
+ (!swcm->prefix[0] ||
+ swcm->prefix[strlen(swcm->prefix)-1]=='/' ?
+ "" : "/"),
+ name->filename);
+ }
+}
+
+void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm)
+{
+ struct sftp_packet *pktin;
+ struct sftp_request *req;
+
+ req = fxp_close_send(swcm->dirh);
+ pktin = sftp_wait_for_reply(req);
+ fxp_close_recv(pktin, req);
+
+ if (swcm->names)
+ fxp_free_names(swcm->names);
+
+ sfree(swcm->prefix);
+ sfree(swcm->wildcard);
+
+ sfree(swcm);
+}
+
+/*
+ * General function to match a potential wildcard in a filename
+ * argument and iterate over every matching file. Used in several
+ * PSFTP commands (rmdir, rm, chmod, mv).
+ */
+int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)
+{
+ char *unwcfname, *newname, *cname;
+ int is_wc, ret;