--- /dev/null
+#include "safeio.h"
+#include "socket.h"
+#include "stringf.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <climits>
+
+static string get_string(FILE *f,const char *fn,const char *what) {
+ char buf[PATH_MAX];
+ char *got=fgets(buf,sizeof(buf),f);
+ if (got!=buf)
+ fatal("Error reading %s from %s: %m",what,fn);
+ char *s(strchr(buf,'\n'));
+ if (!s)
+ fatal("Stored %s in %s is not newline-terminated",what,fn);
+ *s=0;
+ assert(!s[1]);
+ return buf;
+}
+
+// https://stackoverflow.com/a/8615450/2995591
+#include <glob.h> // glob(), globfree()
+#include <cstring> // memset()
+#include <vector>
+#include <string>
+
+int main(int argc,char **argv) {
+ if (argc!=1+3)
+ fatal("streamfer-client <host>:<port> <pattern> <last storage file>");
+ int server_fd(socket_connect(string(argv[1]),0/*retries*/));
+ string pattern(argv[2]);
+ write_safe(server_fd,pattern);
+ string last_found;
+ const char *last_stored_fn(argv[3]);
+ FILE *last_stored_f(fopen(last_stored_fn,"r"));
+ uint64_t offset(0);
+ if (!last_stored_f) {
+ if (errno!=ENOENT)
+ fatal("Error opening filename from %s: %m",last_stored_fn);
+ } else {
+ last_found= get_string(last_stored_f,last_stored_fn,"filename") ;
+ string offsetstr(get_string(last_stored_f,last_stored_fn,"offset" ));
+ char *end;
+ errno=0;
+ unsigned long ul(strtoul(offsetstr.c_str(),&end,0));
+ if (errno||(end&&*end))
+ fatal("Error converting offset from %s: %s",last_stored_fn,offsetstr.c_str());
+ offset=ul;
+ int gotint(fgetc(last_stored_f));
+ if (gotint!=EOF)
+ fatal("Stored filename in %s has excessive data after filename %s and offset %s",last_stored_fn,last_found.c_str(),offsetstr.c_str());
+ int err(fclose(last_stored_f));
+ assert(!err);
+ }
+ write_safe(server_fd,last_found);
+ write_safe(server_fd,offset);
+ string last_got;
+ read_safe(server_fd,last_got);
+ if (last_got.empty()) {
+ warning("No more files to transfer");
+ exit(EXIT_SUCCESS);
+ }
+ struct timespec mtim;
+ read_safe(server_fd,mtim);
+ int file_fd;
+ string file_name;
+ size_t slash(last_got.find_last_of('/'));
+ if (slash!=string::npos)
+ file_name=last_got.substr(slash+1);
+ else
+ file_name=last_got;
+ if (last_found==last_got) {
+ file_fd=open(file_name.c_str(),O_WRONLY);
+ if (file_fd==-1)
+ fatal("Error opening for write %s: %m",file_name.c_str());
+ off_t got(lseek(file_fd,offset,SEEK_SET));
+ if ((uint64_t)got!=offset)
+ fatal("Error seeking in %s to %zu, got: %zu",file_name.c_str(),(size_t)offset,(size_t)got);
+ } else {
+ offset=0;
+ file_fd=open(file_name.c_str(),O_WRONLY|O_CREAT,0644);
+ if (file_fd==-1)
+ fatal("Error creating %s: %m",file_name.c_str());
+ }
+ uint64_t transferred(transfer(server_fd,"server fd",file_fd,file_name.c_str()));
+ if (!transferred)
+ return EXIT_FAILURE;
+ offset+=transferred;
+ struct timespec mtim2[2];
+ mtim2[0]=mtim; // atime
+ mtim2[1]=mtim; // mtime
+ if (futimens(file_fd,mtim2))
+ fatal("Error setting timestamp of %s: %m",file_name.c_str());
+ if (close(file_fd))
+ fatal("Error closing %s: %m",file_name.c_str());
+ int last_stored_fd(open(last_stored_fn,O_WRONLY|O_CREAT|O_TRUNC,0644));
+ if (last_stored_fd==-1)
+ fatal("Error storing filename to %s: %m",last_stored_fn);
+ write_safe(last_stored_fd,last_got.c_str(),last_got.length());
+ write_safe(last_stored_fd,'\n');
+ string offsetstr(stringf("%zu",(size_t)offset));
+ write_safe(last_stored_fd,offsetstr.c_str(),offsetstr.length());
+ write_safe(last_stored_fd,'\n');
+ if (close(last_stored_fd))
+ fatal("Error closing %s: %m",last_stored_fn);
+ return EXIT_SUCCESS;
+}