# # # patch "main.cc" # from [220a72e95c8c0d10669952052d381e1dccc0787a] # to [9268c05179dea0356ec342c0b3458012ca1a7038] # # patch "misc.cc" # from [82fa13081825cc823344ce6b3620b5ab00248ac7] # to [ab49f55cea21542d5152a10dc79ae889f1de621c] # # patch "misc.hh" # from [0dbce437cdb988dc841b1b5e4552e0151ca126aa] # to [6097ccba66ffcf581f1bacdd41bddb43d9f138fc] # # patch "monotone.cc" # from [fcbc08d47a1ccebdf94a2c0b749362f746050814] # to [2034fc498cf2f2a5095b1bc4f6a55ac6e59ce551] # # patch "monotone.hh" # from [e79d886231e39b6487871a4f39c1ced66afc9013] # to [06e3ddf2b1a6c3ef240bed07fd71e19a7989f3ea] # # patch "rev_file_list.cc" # from [3c1249cae4365c63d4560f13c0eefc5d7caac0b1] # to [48f11ed0650a742018b4d84d25b5d617cfbde07c] # # patch "revdat.cc" # from [d382f76625e3a3a0177fd59e0d591440bd0c3a81] # to [33575c525f027f85bd9035135c7d9e6a7ef91515] # ============================================================ --- main.cc 220a72e95c8c0d10669952052d381e1dccc0787a +++ main.cc 9268c05179dea0356ec342c0b3458012ca1a7038 @@ -42,12 +42,6 @@ } }; -void on_delay() -{ - while (Gtk::Main::events_pending()) - Gtk::Main::iteration(); -} - class mainwin : public Gtk::Window { monotone mtn; @@ -73,7 +67,7 @@ void setdb() { Gtk::FileChooserDialog dialog("Please choose a database", - Gtk::FILE_CHOOSER_ACTION_OPEN, ""); + Gtk::FILE_CHOOSER_ACTION_OPEN); dialog.set_transient_for(*this); dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button("Select", Gtk::RESPONSE_OK); @@ -95,7 +89,7 @@ void setdir() { Gtk::FileChooserDialog dialog("Please choose a working copy", - Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, ""); + Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); dialog.set_transient_for(*this); dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button("Select", Gtk::RESPONSE_OK); @@ -124,7 +118,9 @@ } void to_rev() { - std::vector revs = mtn.select(ti.entry.get_text()); + vector revs; + mtn.select(ti.entry.get_text(), revs); + mtn.waitfor(); if (revs.size() == 1) rd.loadrev(revs[0]); } @@ -134,7 +130,6 @@ } mainwin(): rd(&mtn, this) { - mtn.set_longwait_callback(&on_delay); set_default_size(675, 400); ag = Gtk::ActionGroup::create(); ag->add(Gtk::Action::create("Setdir", Gtk::Stock::OPEN, "Set working dir"), ============================================================ --- misc.cc 82fa13081825cc823344ce6b3620b5ab00248ac7 +++ misc.cc ab49f55cea21542d5152a10dc79ae889f1de621c @@ -46,29 +46,11 @@ return Glib::ustring(r[col.name]); } -namespace { - ProgressDialog *pd; - void pd_lwcb() - { - int r = pd->output.rfind("\r"); - int n = pd->output.rfind("\n", r); - if (r != string::npos && n != string::npos) - pd->output = pd->output.substr(0, n+1) + pd->output.substr(r+1); - Glib::RefPtr b = pd->tv.get_buffer(); - b->set_text(pd->output); - Glib::RefPtr t = b->create_tag(); - t->property_family() = "monospace"; - b->apply_tag(t, b->begin(), b->end()); - while (Gtk::Main::events_pending()) - Gtk::Main::iteration(); - } -}; - // mtn->whatever() is called from a timeout so that we'll return first, // and it will be called from the event loop, *after* our window exists, // and can continue to run the event loop itself. ProgressDialog::ProgressDialog(monotone & m) - : mtn(&m), prev_lwcb(m.get_longwait_callback()) + : mtn(&m) { get_vbox()->add(tv); tv.set_editable(false); @@ -76,37 +58,63 @@ cancelbtn = add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); okbtn = add_button("Done", Gtk::RESPONSE_OK); okbtn->set_sensitive(false); - pd = this; - mtn->set_longwait_callback(pd_lwcb); Glib::signal_timeout().connect(sigc::mem_fun(this, &ProgressDialog::timer), 0); } ProgressDialog::~ProgressDialog() { - mtn->set_longwait_callback(prev_lwcb); } bool ProgressDialog::timer() { callmtn(); - pd_lwcb(); okbtn->set_sensitive(true); cancelbtn->set_sensitive(false); return false; } +// Run the event loop while waiting for monotone to finish. +void ProgressDialog::do_wait() +{ + while (mtn->is_busy()) + { + Gtk::Main::iteration(); + string & str(mtn->output_err); + int r = str.rfind("\r"); + int n = str.rfind("\n", r); + if (r != string::npos && n != string::npos) + str = str.substr(0, n+1) + str.substr(r+1); + Glib::RefPtr b = tv.get_buffer(); + b->set_text(str); + Glib::RefPtr t = b->create_tag(); + t->property_family() = "monospace"; + b->apply_tag(t, b->begin(), b->end()); + } +} + +void SyncDialog::callmtn() +{ + mtn->sync(); + do_wait(); +} + void UpdateDialog::callmtn() { std::vector rr; - if (!mtn->update(rr, output)) + mtn->update(rr, output); + do_wait(); + if (!rr.empty()) { chooser c(rr); int result = c.run(); if (result == Gtk::RESPONSE_OK) { - std::string rev = c.result(); + string rev = c.result(); if (!rev.empty()) - mtn->update(rev, output); + { + mtn->update(rev, output); + do_wait(); + } } } } ============================================================ --- misc.hh 0dbce437cdb988dc841b1b5e4552e0151ca126aa +++ misc.hh 6097ccba66ffcf581f1bacdd41bddb43d9f138fc @@ -38,12 +38,13 @@ ~ProgressDialog(); bool timer(); virtual void callmtn() {} + void do_wait(); }; struct SyncDialog : public ProgressDialog { SyncDialog(monotone & m) : ProgressDialog(m) {} - virtual void callmtn() {mtn->sync(output);} + virtual void callmtn(); }; struct UpdateDialog : public ProgressDialog ============================================================ --- monotone.cc fcbc08d47a1ccebdf94a2c0b749362f746050814 +++ monotone.cc 2034fc498cf2f2a5095b1bc4f6a55ac6e59ce551 @@ -1,8 +1,12 @@ // Copyright 2005 Timothy Brownawell // Licensed to the public under the GNU GPL v2, see COPYING #include "monotone.hh" +#include +#ifdef WIN32 +#include +#else #include #include #include @@ -11,10 +15,13 @@ #include #include #include +#endif #include #include +#include +#include // For future reference: CreateProcess and WaitForMultipleObjects on win32 // to replace fork/exec and select? @@ -22,7 +29,7 @@ inline int max(int a, int b) {return (a>b)?a:b;} -monotone::monotone(): pid(-1), dir("."), lwcb(0) +monotone::monotone(): pid(0), dir(".") { } @@ -31,70 +38,132 @@ stop(); } -void -monotone::set_longwait_callback(longwait_callback lc) -{ - lwcb = lc; +namespace { +bool process_packet(string & from, string & out) +{ // cmdnum, err, more, size + int c1 = from.find(':'); + int c2 = from.find(':', c1+1); + int c3 = from.find(':', c2+1); + int c4 = from.find(':', c3+1); + if (c1 == string::npos || c2 == string::npos || + c3 == string::npos || c4 == string::npos) + return false; + if (!(c1 < c2 && c2 < c3 && c3 < c4)) + return false; + string sub = from.substr(c3+1, c4-c3-1); + int size = boost::lexical_cast(sub); + if (size + c4 + 1> from.size()) + return false; + out += from.substr(c4 + 1, size); + bool done = from[c2+1] == 'l'; + from = from.substr(size + c4 + 1); + return done; } +} -bool -monotone::execute(std::vector args) +bool monotone::got_data(Glib::IOCondition c, Glib::RefPtr chan) { - int to_mtn[2]; - int from_mtn[2]; - int err_mtn[2]; - if (pipe(to_mtn) < 0 || pipe(from_mtn) < 0 || pipe(err_mtn) < 0) - return false; - pid = fork(); - if (pid < 0) + if (c != Glib::IO_IN) { - close(to_mtn[0]); - close(to_mtn[1]); - close(from_mtn[0]); - close(from_mtn[1]); - close(err_mtn[0]); - close(err_mtn[1]); + std::cerr<<"&"; + busy = false; return false; } - else if (pid > 0) +// Glib::ustring data; +// chan->read(data, 1040); + gunichar data; + chan->read(data); + if (mode == STDIO) { - close(to_mtn[0]); - close(from_mtn[1]); - close(err_mtn[1]); - to = to_mtn[1]; - from = from_mtn[0]; - errfrom = err_mtn[0]; + tempstr += data; + bool last = process_packet(tempstr, output_std); + if (last) + { + busy = false; + signal_done.emit(); + signal_done.clear(); + output_std.clear(); + output_err.clear(); + } return true; } else { - if (!db.empty()) - args.push_back("--db=" + db); - chdir(dir.c_str()); - if (close(to_mtn[1]) < 0 || close(from_mtn[0]) < 0) - return false; - if (close(0) < 0 || close(1) < 0 || close(2) < 0) - return false; - if (dup2(to_mtn[0], 0) < 0 - || dup2(from_mtn[1], 1) < 0 - || dup2(err_mtn[1], 2) < 0) - return false; - char **arg = new char *[args.size() + 2]; - arg[0] = "monotone"; - for (unsigned int i = 0; i < args.size(); ++i) - { - arg[i+1] = new char[args[i].size()+1]; - memcpy(arg[i+1], args[i].c_str(), args[i].size()+1); - } - arg[args.size()+1] = 0; - int ret = execvp(arg[0], arg); - exit(1); + output_std += data; + return true; } } + +bool monotone::got_err(Glib::IOCondition c, Glib::RefPtr chan) +{ + if (c != Glib::IO_IN) + { + std::cerr<<"*"; + busy = false; + return false; + } +// Glib::ustring data; +// chan->read(data, 1040); + gunichar data; + chan->read(data); + output_err += data; + return true; +} + +void monotone::setup_callbacks() +{ + { + Glib::RefPtr ioc = Glib::IOChannel::create_from_fd(from); + Glib::RefPtr ios = Glib::IOSource::create(ioc, Glib::IO_IN); + ios->connect(sigc::bind(sigc::mem_fun(*this, &monotone::got_data), ioc)); + ios->attach(Glib::MainContext::get_default()); + } + { + Glib::RefPtr ioc = Glib::IOChannel::create_from_fd(errfrom); + Glib::RefPtr ios = Glib::IOSource::create(ioc, Glib::IO_IN); + ios->connect(sigc::bind(sigc::mem_fun(*this, &monotone::got_err), ioc)); + ios->attach(Glib::MainContext::get_default()); + } +} + bool +monotone::execute(vector args) +{std::cerr<<"spawn()\n"; + args.insert(args.begin(), Glib::find_program_in_path("monotone")); + if (!db.empty()) + args.push_back("--db=" + db); + try + { + Glib::spawn_async_with_pipes(dir, args, Glib::SPAWN_DO_NOT_REAP_CHILD, + sigc::slot(), &pid, + &to, &from, &errfrom); +// Glib::signal_child_watch().connect(sigc::mem_fun(*this, &monotone::child_exited), pid); + } + catch (Glib::SpawnError & e) + { + return false; + } + setup_callbacks(); + return true; +} + +void +monotone::child_exited(Glib::Pid p, int c) +{ + pid = 0; + busy = false; + std::cerr< args; args.push_back("automate"); args.push_back("stdio"); @@ -103,454 +172,438 @@ bool monotone::stop() { - if (pid == -1) - return false; + if (!pid) + { + std::cerr<<"Already stopped.\n"; + return false; + } + std::cerr<<"kill()\n"; +#ifdef WIN32 + TerminateProcess(pid, 0); +#else kill(pid, SIGKILL); int r; do {r = waitpid(pid, 0, 0);} while (r==-1 && errno == EINTR); - pid = -1; +#endif + Glib::spawn_close_pid(pid); + pid = 0; + busy = false; return true; } + bool monotone::stopped() { - if (lwcb) - lwcb(); - if (pid == -1) + if (!pid) return true; +#ifdef WIN32 + DWORD r = WaitForSingleObject(pid, 0); + if (r == WAIT_TIMEOUT) + return false; +#else int r = waitpid(pid, 0, WNOHANG); if (r == 0) return false; - pid = -1; +#endif + Glib::spawn_close_pid(pid); + pid = 0; return true; } -bool -monotone::read_header(int & cmdnum, int & err, bool & more, int & size) +void +monotone::waitfor() { - char head[32]; - int got = 0; - int minsize = 8; - int c1(0), c2(0), c3(0), c4(0); - while (!stopped() && !c4) + while(busy) { - int r = read(from, head+got, minsize-got); - got += r; - if (!c1) - for (int i = 0; i < got; ++i) - if (head[i] == ':') - c1 = i, i=got; - if (!c2) - for (int i = c1+1; i < got; ++i) - if (head[i] == ':') - c2 = i, i=got; - if (!c3) - for (int i = c2+1; i < got; ++i) - if (head[i] == ':') - c3 = i, i=got; - if (!c4) - for (int i = c3+1; i < got; ++i) - if (head[i] == ':') - c4 = i, i=got; - if (c4) - minsize = c4; - else if (c3) - minsize = max(c3+2, got+1); - else if (c2) - minsize = max(c2+4, got+3); - else if (c1) - minsize = max(c1+6, got+5); - else - minsize = max(8, got+7); + Gtk::Main::iteration(); } - if (stopped()) - return false; - cmdnum = boost::lexical_cast(std::string(head, c1)); - err = boost::lexical_cast(std::string(head+c1+1, c2-c1-1)); - more = (head[c2+1] == 'm'); - size = boost::lexical_cast(std::string(head+c3+1, c4-c3-1)); - return true; + std::cerr<<"Done waiting.\n"; } -bool -monotone::read_packet(std::string & out) +void +monotone::command(string const & cmd, + vector const & args) { - int cn, er, size; - bool m = false; - if (!read_header(cn, er, m, size)) - return false; - char output[size]; - int got = 0; - do - { - int r = read(from, output, size - got); - got += r; - out += std::string(output, r); - } while (!stopped() && got != size); - if (stopped()) - return false; - return m; -} -std::string -monotone::command(std::string const & cmd, - std::vector const & args) -{ - if (pid == -1) + mode = STDIO; + if (!pid) start(); - std::string res; + busy = true; std::ostringstream s; s << cmd.size() <<":"<::const_iterator i = args.begin(); i != args.end(); ++i) s << i->size() << ":" << *i; std::string c = "l" + s.str() + "e"; - try { - write(to, c.c_str(), c.size()); - while(!stopped() && read_packet(res)) - ; - } catch (std::exception &) { - // something bad happened, assume the monotone process is ****ed up - stop(); - return std::string(); - } - return res; + Glib::RefPtr chan = Glib::IOChannel::create_from_fd(to); + chan->write(c); + chan->flush(); } void -monotone::runcmd(std::string const & cmd, - std::vector const & args, - std::string & out, std::string & err) +monotone::runcmd(string const & cmd, + vector const & args) { - out.clear(); - err.clear(); stop();// stop stdio + busy = true; + mode = EXEC; std::vector argg = args; argg.insert(argg.begin(), cmd); execute(argg); +} - int size = 2048; - char *output = new char[size]; - char *errout = new char[size]; - fd_set rd, ex; - do - { - timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 100000; - FD_ZERO(&rd); - FD_ZERO(&ex); - FD_SET(from, &rd); - FD_SET(errfrom, &rd); - FD_SET(from, &ex); - FD_SET(errfrom, &ex); - int s = ::select(max(from, errfrom)+1, &rd, 0, &ex, &timeout); - if (FD_ISSET(from, &rd)) - { - int r = read(from, output, size); - out += std::string(output, r); - } - if (FD_ISSET(errfrom, &rd)) - { - int r = read(errfrom, errout, size); - err += std::string(errout, r); - } - } while (!stopped()); - int r = read(from, output, size); - out += std::string(output, r); - r = read(errfrom, errout, size); - err += std::string(errout, r); - delete[] output; - delete[] errout; -} +namespace { + void process_inventory(string * resp, vector * outp) + { + string & res(*resp); + vector & out(*outp); + std::cerr<<"inventory... "; + try + { + std::map renames; + int begin = 0; + int end = res.find_first_of("\r\n", begin); + std::cerr<<"(end = "<= 0) + {std::cerr<<"."; + int sp1 = begin + 4; + int sp2 = res.find(' ', sp1 + 1); + int sp3 = res.find(' ', sp2 + 1); + if (sp1 >= res.size() || sp2 == string::npos || sp3 == string::npos) + { + std::cerr<<"!!!"; + begin = -1; + continue; + } + int fromid = boost::lexical_cast(res.substr(sp1, sp2-sp1)); + int toid = boost::lexical_cast(res.substr(sp2+1, sp3-sp2-1)); + std::string path = res.substr(sp3+1, end - sp3-1); + inventory_item *pre, *post; + if (!fromid || !toid) + out.push_back(inventory_item()); + if (!fromid) + pre = &out.back(); + if (!toid) + post = &out.back(); + if (fromid) + { + std::map::iterator i = renames.find(fromid); + if (i != renames.end()) + pre = &out[i->second]; + else + { + renames.insert(std::make_pair(fromid, out.size())); + out.push_back(inventory_item()); + pre = &out.back(); + } + } + if (toid) + { + std::map::iterator i = renames.find(toid); + if (i != renames.end()) + post = &out[i->second]; + else + { + renames.insert(std::make_pair(toid, out.size())); + out.push_back(inventory_item()); + post = &out.back(); + } + } + switch (res[begin])//prestate + { + case 'D': + case 'R': + pre->prename = path; + default: + ; + } + switch (res[begin+1])//poststate + { + case 'R': + case 'A': + post->postname = path; + break; + default: + if (pre->prename.empty()) + { + post->postname = path; + pre->prename = path; + } + } + switch (res[begin+2])//filestate + { + case 'M': + post->state = inventory_item::missing; + break; + case 'P': + post->state = inventory_item::patched; + break; + case 'I': + if (!fromid && !toid) + post->state = inventory_item::ignored; + break; + case 'U': + if (!fromid && !toid) + post->state = inventory_item::unknown; + break; + default: + ; + } + begin = end + 1; + if (end == string::npos) + begin = -1; + end = res.find_first_of("\r\n", begin); + } + } catch (std::exception &) {std::cerr<<"Exception!\n";/*maybe find a way to indicate an error?*/} + std::cerr<<" done.\n"; + } +}; void monotone::inventory(std::vector & out) { out.clear(); - try { std::vector args; - std::string res = command("inventory", args); - std::map renames; - int begin = 0; - int end = res.find('\n', begin); - while (begin != res.size()) - { - int sp1 = begin + 4; - int sp2 = res.find(' ', sp1 + 1); - int sp3 = res.find(' ', sp2 + 1); - int fromid = boost::lexical_cast(res.substr(sp1, sp2-sp1)); - int toid = boost::lexical_cast(res.substr(sp2+1, sp3-sp2-1)); - std::string path = res.substr(sp3+1, end - sp3-1); - inventory_item *pre, *post; - if (!fromid || !toid) - out.push_back(inventory_item()); - if (!fromid) - pre = &out.back(); - if (!toid) - post = &out.back(); - if (fromid) - { - std::map::iterator i = renames.find(fromid); - if (i != renames.end()) - pre = &out[i->second]; - else - { - renames.insert(std::make_pair(fromid, out.size())); - out.push_back(inventory_item()); - pre = &out.back(); - } - } - if (toid) - { - std::map::iterator i = renames.find(toid); - if (i != renames.end()) - post = &out[i->second]; - else - { - renames.insert(std::make_pair(toid, out.size())); - out.push_back(inventory_item()); - post = &out.back(); - } - } - switch (res[begin])//prestate - { - case 'D': - case 'R': - pre->prename = path; - default: - ; - } - switch (res[begin+1])//poststate - { - case 'R': - case 'A': - post->postname = path; - break; - default: - if (pre->prename.empty()) - { - post->postname = path; - pre->prename = path; - } - } - switch (res[begin+2])//filestate - { - case 'M': - post->state = inventory_item::missing; - break; - case 'P': - post->state = inventory_item::patched; - break; - case 'I': - if (!fromid && !toid) - post->state = inventory_item::ignored; - break; - case 'U': - if (!fromid && !toid) - post->state = inventory_item::unknown; - break; - default: - ; - } - begin = end + 1; - end = res.find('\n', begin); - } - } catch (std::exception &) {/*maybe find a way to indicate an error?*/} + command("inventory", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_inventory), &output_std, &out)); } -std::vector -monotone::certs(std::string const & rev) + +namespace { + void process_certs(string * resp, vector * outp) + { + string & res(*resp); + vector & out(*outp); + cert c; + for (int begin = 0, end = res.find('\n'); begin != res.size(); + begin = end + 1, end = res.find('\n', begin)) + { + std::string line = res.substr(begin, end-begin); + int lpos = line.find('"');// other is at end of line + if (lpos == std::string::npos) + { + out.push_back(c); + continue; + } + while (line[line.size()-1] != '"' || line[line.size()-2] =='\\') + { + begin = end + 1; + end = res.find('\n', begin); + line += "\n" + res.substr(begin, end-begin); + } + std::string contents; + for (int i = lpos+1; i < line.size() && line[i] != '"'; ++i) + { + if (line[i] == '\\') + ++i; + contents += line[i]; + } + if (line.find("key") < lpos) + c.key = contents; + else if (line.find("signature") < lpos) + c.sig = (contents == "ok"); + else if (line.find("name") < lpos) + c.name = contents; + else if (line.find("value") < lpos) + c.value = contents; + else if (line.find("trust") < lpos) + c.trusted = (contents == "trusted"); + } + if (res.size()) + out.push_back(c); + } +}; + +void +monotone::certs(std::string const & rev, vector & out) { std::vector args; - std::vector out; args.push_back(rev); - std::string res = command("certs", args); - cert c; - for (int begin = 0, end = res.find('\n'); begin != res.size(); - begin = end + 1, end = res.find('\n', begin)) - { - std::string line = res.substr(begin, end-begin); - int lpos = line.find('"');// other is at end of line - if (lpos == std::string::npos) - { - out.push_back(c); - continue; - } - while (line[line.size()-1] != '"' || line[line.size()-2] =='\\') - { - begin = end + 1; - end = res.find('\n', begin); - line += "\n" + res.substr(begin, end-begin); - } - std::string contents; - for (int i = lpos+1; i < line.size() && line[i] != '"'; ++i) - { - if (line[i] == '\\') - ++i; - contents += line[i]; - } - if (line.find("key") < lpos) - c.key = contents; - else if (line.find("signature") < lpos) - c.sig = (contents == "ok"); - else if (line.find("name") < lpos) - c.name = contents; - else if (line.find("value") < lpos) - c.value = contents; - else if (line.find("trust") < lpos) - c.trusted = (contents == "trusted"); - } - if (res.size()) - out.push_back(c); - return out; + command("certs", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_certs), &output_std, &out)); } -std::vector -monotone::select(std::string const & sel) + +namespace { + void process_select(string * resp, vector * outp) + { + string & res(*resp); + vector & out(*outp); + int begin = 0; + int end = res.find('\n', begin); + while (begin != res.size()) + { + out.push_back(res.substr(begin, end-begin)); + begin = end + 1; + end = res.find('\n', begin); + } + } +}; + +void +monotone::select(string const & sel, vector & out) { - std::vector args, out; + vector args; args.push_back(sel); - std::string res = command("select", args); - int begin = 0; - int end = res.find('\n', begin); - while (begin != res.size()) - { - out.push_back(res.substr(begin, end-begin)); - begin = end + 1; - end = res.find('\n', begin); - } - return out; + command("select", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_select), &output_std, &out)); } + void -monotone::make_cert(std::string const & rev, - std::string const & name, - std::string const & value) +monotone::make_cert(string const & rev, + string const & name, + string const & value) { - std::vector args; + vector args; args.push_back(rev); args.push_back(name); args.push_back(value); - std::string ign1, ign2; - runcmd("cert", args, ign1, ign2); + runcmd("cert", args); } -std::string -monotone::commit(std::vector args) + +namespace { + void process_commit(string * resp, string * outp) + { + string & res(*resp); + string & out(*outp); + int s = 0; + for (int c = 0; c < res.size(); ++c) + { + if (string("abcdefABCDEF0123456789").find(res[c]) == string::npos) + s = c+1; + if (c - s + 1 == 40) + { + out = res.substr(s, 40); + break; + } + } + } +}; + +void +monotone::commit(vector args, string & rev) { - std::string ign1, ign2; - runcmd("commit", args, ign1, ign2); - args.clear(); - std::string res = command("get_revision", args); - for (int begin = 0, end = res.find('\n'); begin != res.size(); - begin = end + 1, end = res.find('\n', begin)) - { - std::string line = res.substr(begin, end-begin); - int lpos = line.find_first_of("[\""); - int rpos = line.find_first_of("]\"", lpos + 1); - std::string contents = line.substr(lpos + 1, rpos - lpos - 1); - if (line.find("old_revision") < lpos) - return contents; - } - return std::string(); + runcmd("commit", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_commit), &output_err, &rev)); } -std::string -monotone::diff(std::string const & filename) + +namespace { + void process_noop(string * from, string * to) + { + *to = *from; + } +}; + +void +monotone::diff(std::string const & filename, string & out) { - std::string out, ign; std::vector args; args.push_back(filename); - runcmd("diff", args, out, ign); - return out; + runcmd("diff", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_noop), &output_std, &out)); } -std::string -monotone::diff(std::string const & filename, - std::string const & rev1, - std::string const & rev2) + +void +monotone::diff(string const & filename, + string const & rev1, + string const & rev2, + string & out) { std::vector args; args.push_back(filename); args.push_back("--revision=" + rev1); args.push_back("--revision=" + rev2); - std::string out, ign; - runcmd("diff", args, out, ign); - return out; + runcmd("diff", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_noop), &output_std, &out)); } -std::string -monotone::cat(std::string const & filename, std::string const & rev) + +void +monotone::cat(string const & filename, string const & rev, string & out) { - std::vector args; + vector args; args.push_back(filename); args.push_back("--revision=" + rev); - std::string out, ign; - runcmd("cat", args, out, ign); - return out; + runcmd("cat", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_noop), &output_std, &out)); } -std::string -monotone::get_revision(std::string const & rev) + +void +monotone::get_revision(string const & rev, string & out) { std::vector args; args.push_back(rev); - return command("get_revision", args); + command("get_revision", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_noop), &output_std, &out)); } -std::string -monotone::get_manifest(std::string const & rev) + +void +monotone::get_manifest(string const & rev, string & out) { std::vector args; args.push_back(rev); - return command("get_manifest", args); + command("get_manifest", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_noop), &output_std, &out)); } + void -monotone::add(std::string const & file) +monotone::add(string const & file) { - std::string ign1, ign2; - std::vector args; + vector args; args.push_back(file); - runcmd("add", args, ign1, ign2); + runcmd("add", args); } + void -monotone::drop(std::string const & file) +monotone::drop(string const & file) { - std::string ign1, ign2; - std::vector args; + vector args; args.push_back(file); - runcmd("drop", args, ign1, ign2); + runcmd("drop", args); } + void -monotone::revert(std::string const & file) +monotone::revert(string const & file) { - std::string ign1, ign2; - std::vector args; + vector args; args.push_back(file); - runcmd("revert", args, ign1, ign2); + runcmd("revert", args); } + void -monotone::rename(std::string const & oldname, std::string const & newname) +monotone::rename(string const & oldname, string const & newname) { - std::string ign1, ign2; - std::vector args; + vector args; args.push_back("--execute"); args.push_back(oldname); args.push_back(newname); - runcmd("rename", args, ign1, ign2); + runcmd("rename", args); } -bool -monotone::update(std::vector & opts, string & out) +namespace { + void process_update(vector * opts, string * in, string * out) + { + opts->clear(); + *out = *in; + int p = out->find("multiple update candidates"); + if (p == std::string::npos) + return; + + p = out->find("monotone: ", p + 1); + while (p != std::string::npos) + { + p = out->find(":", p); + p = out->find_first_not_of(" ", p + 1); + opts->push_back(out->substr(p, 40)); + p = out->find("monotone: ", p + 1); + } + } +}; + +void +monotone::update(vector & opts, string & out) { opts.clear(); std::string ign; std::vector args; - runcmd("update", args, ign, out); - int p = out.find("multiple update candidates"); - if (p == std::string::npos) - return true; - - p = out.find("monotone: ", p + 1); - while (p != std::string::npos) - { - p = out.find(":", p); - p = out.find_first_not_of(" ", p + 1); - opts.push_back(out.substr(p, 40)); - p = out.find("monotone: ", p + 1); - } - return false; + runcmd("update", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_update), &opts, &output_err, &out)); } void @@ -559,14 +612,15 @@ std::string ign; std::vector args; args.push_back("--revision="+rev); - runcmd("update", args, ign, out); + runcmd("update", args); + signal_done.connect(sigc::bind(sigc::ptr_fun(&process_noop), &output_err, &out)); } void -monotone::sync(string & res) +monotone::sync() { string ign; vector args; args.push_back("--ticker=count"); - runcmd("sync", args, ign, res); + runcmd("sync", args); } ============================================================ --- monotone.hh e79d886231e39b6487871a4f39c1ced66afc9013 +++ monotone.hh 06e3ddf2b1a6c3ef240bed07fd71e19a7989f3ea @@ -11,6 +11,7 @@ #ifndef __MONOTONE_HH_ #define __MONOTONE_HH_ +#include #include #include using std::string; @@ -30,70 +31,81 @@ struct cert { - std::string key; + string key; bool sig; - std::string name; - std::string value; + string name; + string value; bool trusted; }; -typedef void(*longwait_callback)(); - class monotone { - pid_t pid; - std::string dir;// chdir here before exec - std::string db;// if not empty, add --db= to argument list for exec + enum Mode {STDIO, EXEC}; + Mode mode; + Glib::Pid pid; + string dir;// chdir here before exec + string db;// if not empty, add --db= to argument list for exec int from; int errfrom; int to; + bool busy; - bool execute(std::vector args); + bool execute(vector args); bool start(); bool stop(); bool stopped(); - bool read_header(int & cmdnum, int & err, bool & more, int & size); - bool read_packet(std::string & out); - longwait_callback lwcb; + + void child_exited(Glib::Pid p, int c); + void setup_callbacks(); + bool got_data(Glib::IOCondition c, Glib::RefPtr chan); + bool got_err(Glib::IOCondition c, Glib::RefPtr chan); + + string tempstr; + + sigc::signal signal_done; + public: + string output_std, output_err; + monotone(); ~monotone(); - void set_longwait_callback(longwait_callback lc); - longwait_callback get_longwait_callback(){return lwcb;} - void set_dir(std::string const & s){dir = (s.empty()?".":s); stop();} - void set_db(std::string const & s){db = s; stop();} - std::string get_dir(){return dir;} - std::string get_db(){return db;} + void set_dir(string const & s){dir = (s.empty()?".":s); stop();} + void set_db(string const & s){db = s; stop();} + string get_dir(){return dir;} + string get_db(){return db;} + // run the Gtk event loop while waiting for the current command to finish + void waitfor(); + // call this when the current command finished + void when_done(sigc::slot cb); + bool is_busy() {return busy;} + // run a command with 'automate stdio' - std::string command(std::string const & cmd, - std::vector const & args); + void command(string const & cmd, vector const & args); // run a command from the command line - void runcmd(std::string const & cmd, - std::vector const & args, - std::string & out, std::string & err); + void runcmd(string const & cmd, vector const & args); + void inventory(std::vector & out); - std::vector certs(std::string const & rev); - std::vector select(std::string const & sel); + void certs(std::string const & rev, vector & out); + void select(std::string const & sel, vector & out); void make_cert(std::string const & rev, std::string const & name, std::string const & value); - std::string commit(std::vector args); - std::string diff(std::string const & filename); - std::string diff(std::string const & filename, - std::string const & rev1, - std::string const & rev2); - std::string cat(std::string const & filename, std::string const & rev); - std::string get_revision(std::string const & rev); - std::string get_manifest(std::string const & rev); - void add(std::string const & file); - void drop(std::string const & file); - void revert(std::string const & file); - void rename(std::string const & oldname, std::string const & newname); - bool update(std::vector & opts, string & out); - void update(std::string const & rev, string & out); - void sync(string & res); + void commit(vector args, string & out); + void diff(string const & filename, string & out); + void diff(string const & filename, string const & rev1, + string const & rev2, string & out); + void cat(string const & filename, string const & rev, string & out); + void get_revision(string const & rev, string & out); + void get_manifest(string const & rev, string & out); + void add(string const & file); + void drop(string const & file); + void revert(string const & file); + void rename(string const & oldname, string const & newname); + void update(vector & opts, string & out); + void update(string const & rev, string & out); + void sync(); }; #endif ============================================================ --- rev_file_list.cc 3c1249cae4365c63d4560f13c0eefc5d7caac0b1 +++ rev_file_list.cc 48f11ed0650a742018b4d84d25b5d617cfbde07c @@ -321,7 +321,10 @@ if (r.empty()) certs.clear(); else - certs = rd->mtn->certs(r); + { + rd->mtn->certs(r, certs); + rd->mtn->waitfor(); + } } void rev_file_list::pchange(int n) @@ -411,10 +414,13 @@ if (row[col.status] != states::added && row[col.status] != states::ignored && row[col.status] != states::unknown) { + string diff; if (wc) - rd->rfi.set_diff(rd->mtn->diff(filename)); + rd->mtn->diff(filename, diff); else - rd->rfi.set_diff(rd->mtn->diff(filename, parent, rev)); + rd->mtn->diff(filename, parent, rev, diff); + rd->mtn->waitfor(); + rd->rfi.set_diff(diff); } else rd->rfi.set_diff("No diff available."); @@ -422,7 +428,12 @@ if (wc) rd->rfi.set_contents(readfile(fullname)); else if (row[col.status] != states::dropped) - rd->rfi.set_contents(rd->mtn->cat(filename, rev)); + { + string foo; + rd->mtn->cat(filename, rev, foo); + rd->mtn->waitfor(); + rd->rfi.set_contents(foo); + } else rd->rfi.set_contents("File dropped -- contents not available"); ============================================================ --- revdat.cc d382f76625e3a3a0177fd59e0d591440bd0c3a81 +++ revdat.cc 33575c525f027f85bd9035135c7d9e6a7ef91515 @@ -150,7 +150,8 @@ if (rfl.get_wc()) { args.push_back("--message=" + msg); - rev = mtn->commit(args); + mtn->commit(args, rev); + mtn->waitfor(); } for (std::map::iterator i = comments.begin(); i != comments.end(); ++i) @@ -172,6 +173,7 @@ rfl.set_wc(true); std::vector res; mtn->inventory(res); + mtn->waitfor(); rfl.set_files(res); } @@ -190,7 +192,9 @@ rfl.set_wc(false); std::vector pvec; std::vector > pchanges; - std::string res = mtn->get_revision(rev); + string res; + mtn->get_revision(rev, res); + mtn->waitfor(); std::string rename_from, man; std::set changed; std::map pmap; @@ -256,7 +260,8 @@ pchanges.back()[pos].state = inventory_item::patched; } } - res = mtn->get_manifest(man); + mtn->get_manifest(man, res); + mtn->waitfor(); for (int begin = 0, end = res.find('\n'); begin != res.size(); begin = end + 1, end = res.find('\n', begin)) {