// Syncify // // Douglas Thrift // // $Id$ #include "Syncify.hpp" #include "SambaReader.hpp" #include #include #include int Main(const app::Options& options) { ext::String local("."), remote, pattern("^.*$"); _foreach (const app::ArgumentList, arg, app::GetArguments()) { Matcher matcher; if (*arg == matcher("^-local=(.+)$")) local = matcher[1]; else if (*arg == matcher("^-remote=(.+)$")) remote = matcher[1]; else if (*arg == matcher("^-pattern=(.+)$")) pattern = matcher[1]; else if (*arg == "-D") Syncify::debug = true; else { api::Cerr << "Usage: " << Syncify::program << " [-local=local] -remote=remote [-pattern=pattern] [-D]" << ios::NewLine; return 1; } } if (remote.IsEmpty()) { api::Cerr << "Usage: " << Syncify::program << " [-local=local] -remote=remote [-pattern=pattern] [-D]" << ios::NewLine; return 1; } Syncify syncify(local, remote, pattern); return 0; } extern "C" { void authenticate(const char* srv, const char* shr, char* wg, int wglen, char* un, int unlen, char* pw, int pwlen) { static ext::Buffer user("Douglas Thrift"), password(::getpass("Password:")); _foreach (ext::Buffer, atom, user) un[_index] = *atom; _foreach (ext::Buffer, atom, password) pw[_index] = *atom; un[user.GetSize() < size_t(unlen) ? user.GetSize() : unlen - 1] = '\0'; pw[password.GetSize() < size_t(pwlen) ? password.GetSize() : pwlen - 1] = '\0'; } } Syncify::Syncify(const ext::String& local, const ext::String& remote, const ext::String& pattern) : pattern(pattern), transfering(true), transferers(etl::BindAll(&Syncify::transfer, this)) { CheckError(::smbc_init(authenticate, debug ? 2 : 0)); transferers.Add(1); syncify(local, remote); transfering = false; } Syncify::~Syncify() { transferers.Join(); api::Cout << ios::NewLine; ::smbc_free_context(::smbc_set_context(NULL), 1); } ext::String Syncify::program(api::GetExecutablePath().GetName()); bool Syncify::debug(false); void Syncify::syncify(const api::Path& local, const ext::String& remote) { try { Matcher dots("^\\.{1,2}$"); ext::RedBlackSet directories; int directory(CheckError(::smbc_opendir(remote.NullTerminate()))); for (::smbc_dirent* entity(::smbc_readdir(directory)); entity != NULL; entity = ::smbc_readdir(directory)) switch (entity->smbc_type) { case SMBC_FILE: if (entity->name == pattern) { api::Path path(local.GetChild(entity->name)); if (!path.Exists()) { api::Cout << "+" << entity->name << ios::Flush; ext::Buffer buffer(entity->name); buffer.SetSize(buffer.GetSize() * 3); ::smbc_urlencode(buffer.Begin(), entity->name, buffer.GetSize()); _synchronized (transfersLock) transfers.push(Transfer(path.GetPath(), remote + "/" + ext::String(buffer))); } else api::Cout << "-" << ios::Flush; } break; case SMBC_DIR: case SMBC_LINK: if (entity->name != dots) directories.Insert(entity->name); } ::smbc_closedir(directory); _foreach (const ext::RedBlackSet, directory, directories) syncify(local.GetChild(*directory), remote + "/" + *directory); } catch (const Error& error) {} } int Syncify::transfer() { _synchronized (transfersLock) while (transfering || !transfers.empty()) if (!transfers.empty()) { Transfer transfer(transfers.front()); transfers.pop(); _desynchronized (transfersLock) { { api::Path file(transfer.local); ext::Stack stack; for (api::Path path(file.GetParent()); !path.Exists(); path = path.GetParent()) stack.Push(path.GetPath()); while (!stack.IsEmpty()) { try { api::Posix::CheckError(::mkdir(stack.Top().NullTerminate(), 0755)); } catch (const api::Posix::Error&) {} stack.Pop(); } } try { SambaReader samba(transfer.remote); api::FileWriter file(transfer.local); ios::ReadToWrite(samba, file); } catch (const Error& error) { api::Cerr << error << ios::NewLine; } } } else _desynchronized (transfersLock) { ::timespec wait = { 0, 1000000 }; ::nanosleep(&wait, NULL); } return 0; }