2008-03-19 12:29:14 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 Patrick Ohly
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY, TITLE, NONINFRINGEMENT or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
* 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "SyncEvolutionCmdline.h"
|
|
|
|
#include "FilterConfigNode.h"
|
|
|
|
#include "VolatileConfigNode.h"
|
|
|
|
#include "EvolutionSyncSource.h"
|
|
|
|
#include "EvolutionSyncClient.h"
|
|
|
|
#include "SyncEvolutionUtil.h"
|
|
|
|
|
2008-03-19 17:43:31 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2008-03-19 12:29:14 +01:00
|
|
|
#include <iostream>
|
2008-03-28 23:32:00 +01:00
|
|
|
#include <sstream>
|
2008-03-19 12:29:14 +01:00
|
|
|
#include <memory>
|
|
|
|
#include <set>
|
2008-03-19 15:35:22 +01:00
|
|
|
#include <algorithm>
|
2008-03-19 12:29:14 +01:00
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#include <boost/shared_ptr.hpp>
|
2008-03-29 16:41:11 +01:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2008-03-19 12:29:14 +01:00
|
|
|
|
2008-03-29 15:16:34 +01:00
|
|
|
SyncEvolutionCmdline::SyncEvolutionCmdline(int argc, const char * const * argv, ostream &out, ostream &err) :
|
2008-03-19 12:29:14 +01:00
|
|
|
m_argc(argc),
|
|
|
|
m_argv(argv),
|
|
|
|
m_out(out),
|
|
|
|
m_err(err),
|
|
|
|
m_validSyncProps(EvolutionSyncConfig::getRegistry()),
|
|
|
|
m_validSourceProps(EvolutionSyncSourceConfig::getRegistry())
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool SyncEvolutionCmdline::parse()
|
|
|
|
{
|
|
|
|
int opt = 1;
|
|
|
|
while (opt < m_argc) {
|
|
|
|
if (m_argv[opt][0] != '-') {
|
|
|
|
break;
|
|
|
|
}
|
2008-03-29 16:41:11 +01:00
|
|
|
if (boost::iequals(m_argv[opt], "--sync") ||
|
|
|
|
boost::iequals(m_argv[opt], "-s")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
opt++;
|
|
|
|
string param;
|
|
|
|
string cmdopt(m_argv[opt - 1]);
|
2008-03-19 13:11:39 +01:00
|
|
|
if (!parseProp(m_validSourceProps, m_sourceProps,
|
2008-03-19 12:29:14 +01:00
|
|
|
m_argv[opt - 1], opt == m_argc ? NULL : m_argv[opt],
|
|
|
|
"sync")) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--sync-property") ||
|
|
|
|
boost::iequals(m_argv[opt], "-y")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
opt++;
|
|
|
|
if (!parseProp(m_validSyncProps, m_syncProps,
|
|
|
|
m_argv[opt - 1], opt == m_argc ? NULL : m_argv[opt])) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--source-property") ||
|
|
|
|
boost::iequals(m_argv[opt], "-z")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
opt++;
|
|
|
|
if (!parseProp(m_validSourceProps, m_sourceProps,
|
|
|
|
m_argv[opt - 1], opt == m_argc ? NULL : m_argv[opt])) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--properties") ||
|
|
|
|
boost::iequals(m_argv[opt], "-r")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
opt++;
|
|
|
|
/* TODO */
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--template") ||
|
|
|
|
boost::iequals(m_argv[opt], "-l")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
opt++;
|
|
|
|
if (opt >= m_argc) {
|
|
|
|
usage(true, string("missing parameter for ") + cmdOpt(m_argv[opt - 1]));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_template = m_argv[opt];
|
2008-03-19 17:43:31 +01:00
|
|
|
if (m_template == "?") {
|
|
|
|
dumpServers("Available configuration templates:",
|
|
|
|
EvolutionSyncConfig::getServerTemplates());
|
|
|
|
m_dontrun = true;
|
|
|
|
}
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--print-servers")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_printServers = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--print-config") ||
|
|
|
|
boost::iequals(m_argv[opt], "-p")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_printConfig = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--configure") ||
|
|
|
|
boost::iequals(m_argv[opt], "-c")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_configure = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--migrate")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_migrate = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--status") ||
|
|
|
|
boost::iequals(m_argv[opt], "-t")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_status = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--quiet") ||
|
|
|
|
boost::iequals(m_argv[opt], "-q")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_quiet = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--help") ||
|
|
|
|
boost::iequals(m_argv[opt], "-h")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_usage = true;
|
2008-03-29 16:41:11 +01:00
|
|
|
} else if(boost::iequals(m_argv[opt], "--version")) {
|
2008-03-19 12:29:14 +01:00
|
|
|
m_version = true;
|
2008-03-19 13:11:39 +01:00
|
|
|
} else {
|
|
|
|
usage(false, string(m_argv[opt]) + ": unknown parameter");
|
|
|
|
return false;
|
2008-03-19 12:29:14 +01:00
|
|
|
}
|
|
|
|
opt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opt < m_argc) {
|
|
|
|
m_server = m_argv[opt++];
|
|
|
|
while (opt < m_argc) {
|
|
|
|
m_sources.insert(m_argv[opt++]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SyncEvolutionCmdline::run() {
|
|
|
|
if (m_usage) {
|
|
|
|
usage(true);
|
|
|
|
} else if (m_version) {
|
|
|
|
printf("SyncEvolution %s\n", VERSION);
|
2008-03-19 17:43:31 +01:00
|
|
|
} else if (m_printServers || m_server == "?") {
|
|
|
|
dumpServers("Configured servers:",
|
|
|
|
EvolutionSyncConfig::getServers());
|
2008-03-19 12:29:14 +01:00
|
|
|
} else if (m_dontrun) {
|
|
|
|
// user asked for information
|
|
|
|
} else if (m_argc == 1) {
|
|
|
|
const SourceRegistry ®istry(EvolutionSyncSource::getSourceRegistry());
|
|
|
|
boost::shared_ptr<FilterConfigNode> configNode(new VolatileConfigNode());
|
|
|
|
boost::shared_ptr<FilterConfigNode> hiddenNode(new VolatileConfigNode());
|
|
|
|
boost::shared_ptr<FilterConfigNode> trackingNode(new VolatileConfigNode());
|
|
|
|
SyncSourceNodes nodes(configNode, hiddenNode, trackingNode);
|
|
|
|
EvolutionSyncSourceParams params("list", nodes, "");
|
|
|
|
|
|
|
|
for (SourceRegistry::const_iterator source = registry.begin();
|
|
|
|
source != registry.end();
|
|
|
|
++source) {
|
|
|
|
for (Values::const_iterator alias = (*source)->m_typeValues.begin();
|
|
|
|
alias != (*source)->m_typeValues.end();
|
|
|
|
++alias) {
|
|
|
|
if (!alias->empty() && (*source)->m_enabled) {
|
|
|
|
configNode->setProperty("type", *alias->begin());
|
|
|
|
auto_ptr<EvolutionSyncSource> source(EvolutionSyncSource::createSource(params, false));
|
|
|
|
if (source.get() != NULL) {
|
|
|
|
listSources(*source, join(" = ", alias->begin(), alias->end()));
|
|
|
|
cout << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
usage(false);
|
|
|
|
} else if (m_printConfig) {
|
2008-03-19 15:35:22 +01:00
|
|
|
boost::shared_ptr<EvolutionSyncConfig> config;
|
|
|
|
|
|
|
|
if (m_template.empty()) {
|
|
|
|
config.reset(new EvolutionSyncConfig(m_server));
|
|
|
|
if (!config->exists()) {
|
|
|
|
cerr << "ERROR: server '" << m_server << "' has not been configured yet." << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
config = EvolutionSyncConfig::createServerTemplate(m_template);
|
|
|
|
if (!config.get()) {
|
|
|
|
cerr << "ERROR: no configuration template for '" << m_template << "' available." << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-19 12:29:14 +01:00
|
|
|
if (m_sources.empty()) {
|
2008-03-19 15:35:22 +01:00
|
|
|
boost::shared_ptr<FilterConfigNode> syncProps(config->getProperties());
|
2008-03-19 12:29:14 +01:00
|
|
|
syncProps->setFilter(m_syncProps);
|
2008-03-19 15:35:22 +01:00
|
|
|
dumpProperties(*syncProps, config->getRegistry());
|
2008-03-19 12:29:14 +01:00
|
|
|
}
|
|
|
|
|
2008-03-19 15:35:22 +01:00
|
|
|
list<string> sources = config->getSyncSources();
|
2008-03-19 12:29:14 +01:00
|
|
|
for (list<string>::const_iterator it = sources.begin();
|
|
|
|
it != sources.end();
|
|
|
|
++it) {
|
|
|
|
if (m_sources.empty() ||
|
|
|
|
m_sources.find(*it) != m_sources.end()) {
|
|
|
|
m_out << endl << "[" << *it << "]" << endl;
|
2008-03-20 23:05:10 +01:00
|
|
|
ConstSyncSourceNodes nodes = config->getSyncSourceNodes(*it);
|
|
|
|
boost::shared_ptr<FilterConfigNode> sourceProps(new FilterConfigNode(boost::shared_ptr<const ConfigNode>(nodes.m_configNode)));
|
2008-03-19 12:29:14 +01:00
|
|
|
sourceProps->setFilter(m_sourceProps);
|
2008-03-20 23:05:10 +01:00
|
|
|
dumpProperties(*sourceProps, EvolutionSyncSourceConfig::getRegistry());
|
2008-03-19 12:29:14 +01:00
|
|
|
}
|
|
|
|
}
|
2008-03-19 15:35:22 +01:00
|
|
|
} else if (m_server == "" && m_argc > 1) {
|
|
|
|
// Options given, but no server - not sure what the user wanted?!
|
|
|
|
usage(true, "server name missing");
|
|
|
|
return false;
|
2008-03-19 12:29:14 +01:00
|
|
|
} else if (m_configure || m_migrate) {
|
2008-03-19 17:43:31 +01:00
|
|
|
// Both config changes and migration are implemented as copying from
|
|
|
|
// another config (template resp. old one). Migration also moves
|
|
|
|
// the old config.
|
|
|
|
boost::shared_ptr<EvolutionSyncConfig> from;
|
|
|
|
if (m_migrate) {
|
|
|
|
from.reset(new EvolutionSyncConfig(m_server));
|
|
|
|
if (!from->exists()) {
|
|
|
|
cerr << "ERROR: server '" << m_server << "' has not been configured yet." << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int counter = 0;
|
|
|
|
string oldRoot = from->getRootPath();
|
|
|
|
string suffix;
|
|
|
|
while (true) {
|
|
|
|
string newname;
|
|
|
|
ostringstream newsuffix;
|
|
|
|
newsuffix << ".old";
|
|
|
|
if (counter) {
|
|
|
|
newsuffix << "." << counter;
|
|
|
|
}
|
|
|
|
suffix = newsuffix.str();
|
|
|
|
newname = oldRoot + suffix;
|
|
|
|
if (!rename(oldRoot.c_str(),
|
|
|
|
newname.c_str())) {
|
|
|
|
break;
|
|
|
|
} else if (errno != EEXIST && errno != ENOTEMPTY) {
|
|
|
|
m_err << "ERROR: renaming " << oldRoot << " to " <<
|
|
|
|
newname << ": " << strerror(errno) << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
from.reset(new EvolutionSyncConfig(m_server + suffix));
|
|
|
|
} else {
|
|
|
|
from.reset(new EvolutionSyncConfig(m_server));
|
|
|
|
if (!from->exists()) {
|
|
|
|
// creating from scratch, look for template
|
|
|
|
string configTemplate = m_template.empty() ? m_server : m_template;
|
|
|
|
from = EvolutionSyncConfig::createServerTemplate(configTemplate);
|
|
|
|
if (!from.get()) {
|
|
|
|
cerr << "ERROR: no configuration template for '" << configTemplate << "' available." << endl;
|
|
|
|
dumpServers("Available configuration templates:",
|
|
|
|
EvolutionSyncConfig::getServerTemplates());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply config changes on-the-fly
|
|
|
|
from->setConfigFilter(true, m_syncProps);
|
|
|
|
from->setConfigFilter(false, m_sourceProps);
|
|
|
|
|
|
|
|
// write into the requested configuration, creating it if necessary
|
|
|
|
boost::shared_ptr<EvolutionSyncConfig> to(new EvolutionSyncConfig(m_server));
|
2008-03-20 23:05:10 +01:00
|
|
|
to->copy(*from, (m_migrate || m_sources.empty()) ? NULL : &m_sources);
|
2008-03-19 17:43:31 +01:00
|
|
|
|
|
|
|
// done, now write it
|
|
|
|
to->flush();
|
2008-03-19 12:29:14 +01:00
|
|
|
} else {
|
|
|
|
EvolutionSyncClient client(m_server, true, m_sources);
|
|
|
|
client.setQuiet(m_quiet);
|
|
|
|
client.setConfigFilter(true, m_syncProps);
|
|
|
|
client.setConfigFilter(false, m_sourceProps);
|
|
|
|
if (m_status) {
|
|
|
|
client.status();
|
|
|
|
} else {
|
|
|
|
client.sync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
string SyncEvolutionCmdline::cmdOpt(const char *opt, const char *param)
|
|
|
|
{
|
|
|
|
string res = "'";
|
|
|
|
res += opt;
|
|
|
|
if (param) {
|
|
|
|
res += " ";
|
|
|
|
res += param;
|
|
|
|
}
|
|
|
|
res += "'";
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SyncEvolutionCmdline::parseProp(const ConfigPropertyRegistry &validProps,
|
|
|
|
FilterConfigNode::ConfigFilter &props,
|
|
|
|
const char *opt,
|
|
|
|
const char *param,
|
|
|
|
const char *propname)
|
|
|
|
{
|
|
|
|
if (!param) {
|
|
|
|
usage(true, string("missing parameter for ") + cmdOpt(opt, param));
|
|
|
|
return false;
|
|
|
|
} else if (!strcmp(param, "?")) {
|
|
|
|
m_dontrun = true;
|
|
|
|
if (propname) {
|
|
|
|
return listPropValues(validProps, propname, opt);
|
|
|
|
} else {
|
|
|
|
return listProperties(validProps, opt);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
string propstr;
|
|
|
|
string paramstr;
|
|
|
|
if (propname) {
|
|
|
|
propstr = propname;
|
|
|
|
paramstr = param;
|
|
|
|
} else {
|
|
|
|
const char *equal = strchr(param, '=');
|
|
|
|
if (!equal) {
|
|
|
|
usage(true, string("the '=<value>' part is missing in: ") + cmdOpt(opt, param));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
propstr.assign(param, equal - param);
|
|
|
|
paramstr.assign(equal + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paramstr == "?") {
|
|
|
|
m_dontrun = true;
|
|
|
|
return listPropValues(validProps, propstr, cmdOpt(opt, param));
|
|
|
|
} else {
|
|
|
|
const ConfigProperty *prop = validProps.find(propstr);
|
|
|
|
if (!prop) {
|
2008-03-19 13:11:39 +01:00
|
|
|
m_err << "ERROR: " << cmdOpt(opt, param) << ": no such property" << endl;
|
2008-03-19 12:29:14 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
string error;
|
|
|
|
if (!prop->checkValue(paramstr, error)) {
|
|
|
|
m_err << "ERROR: " << cmdOpt(opt, param) << ": " << error << endl;
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
props.set(propstr, paramstr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SyncEvolutionCmdline::listPropValues(const ConfigPropertyRegistry &validProps,
|
|
|
|
const string &propName,
|
|
|
|
const string &opt)
|
|
|
|
{
|
|
|
|
const ConfigProperty *prop = validProps.find(propName);
|
|
|
|
if (!prop) {
|
2008-03-19 13:11:39 +01:00
|
|
|
m_err << "ERROR: "<< opt << ": no such property" << endl;
|
2008-03-19 12:29:14 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
m_out << opt << endl;
|
|
|
|
string comment = prop->getComment();
|
|
|
|
|
|
|
|
if (comment != "") {
|
|
|
|
list<string> commentLines;
|
|
|
|
ConfigProperty::splitComment(comment, commentLines);
|
|
|
|
for (list<string>::const_iterator line = commentLines.begin();
|
|
|
|
line != commentLines.end();
|
|
|
|
++line) {
|
|
|
|
m_out << " " << *line << endl;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_out << " no documentation available";
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SyncEvolutionCmdline::listProperties(const ConfigPropertyRegistry &validProps,
|
|
|
|
const string &opt)
|
|
|
|
{
|
|
|
|
// The first of several related properties has a comment.
|
|
|
|
// Remember that comment and print it as late as possible,
|
|
|
|
// that way related properties preceed their comment.
|
|
|
|
string comment;
|
|
|
|
for (ConfigPropertyRegistry::const_iterator prop = validProps.begin();
|
|
|
|
prop != validProps.end();
|
|
|
|
++prop) {
|
|
|
|
if (!(*prop)->isHidden()) {
|
|
|
|
string newComment = (*prop)->getComment();
|
|
|
|
|
|
|
|
if (newComment != "") {
|
|
|
|
dumpComment(m_out, " ", comment);
|
|
|
|
m_out << endl;
|
|
|
|
comment = newComment;
|
|
|
|
}
|
|
|
|
m_out << (*prop)->getName() << ":" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dumpComment(m_out, " ", comment);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncEvolutionCmdline::listSources(EvolutionSyncSource &syncSource, const string &header)
|
|
|
|
{
|
|
|
|
m_out << header << ":\n";
|
|
|
|
EvolutionSyncSource::sources sources = syncSource.getSyncBackends();
|
|
|
|
|
|
|
|
for (EvolutionSyncSource::sources::const_iterator it = sources.begin();
|
|
|
|
it != sources.end();
|
|
|
|
it++) {
|
|
|
|
m_out << " " << it->m_name << " (" << it->m_uri << ")\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-19 17:43:31 +01:00
|
|
|
void SyncEvolutionCmdline::dumpServers(const string &preamble,
|
|
|
|
const EvolutionSyncConfig::ServerList &servers)
|
|
|
|
{
|
|
|
|
m_out << preamble << endl;
|
|
|
|
for (EvolutionSyncConfig::ServerList::const_iterator it = servers.begin();
|
|
|
|
it != servers.end();
|
|
|
|
++it) {
|
|
|
|
m_out << " " << it->first << " = " << it->second << endl;
|
|
|
|
}
|
|
|
|
if (!servers.size()) {
|
|
|
|
m_out << " none" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-19 12:29:14 +01:00
|
|
|
void SyncEvolutionCmdline::dumpProperties(const ConfigNode &configuredProps,
|
|
|
|
const ConfigPropertyRegistry &allProps)
|
|
|
|
{
|
|
|
|
for (ConfigPropertyRegistry::const_iterator it = allProps.begin();
|
|
|
|
it != allProps.end();
|
|
|
|
++it) {
|
2008-03-19 17:43:31 +01:00
|
|
|
if ((*it)->isHidden()) {
|
|
|
|
continue;
|
|
|
|
}
|
2008-03-19 12:29:14 +01:00
|
|
|
if (!m_quiet) {
|
2008-03-19 15:38:19 +01:00
|
|
|
string comment = (*it)->getComment();
|
|
|
|
if (!comment.empty()) {
|
|
|
|
m_out << endl;
|
|
|
|
dumpComment(m_out, "# ", comment);
|
|
|
|
}
|
2008-03-19 12:29:14 +01:00
|
|
|
}
|
|
|
|
m_out << (*it)->getName() << " = " << (*it)->getProperty(configuredProps) << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncEvolutionCmdline::dumpComment(ostream &stream,
|
|
|
|
const string &prefix,
|
|
|
|
const string &comment)
|
|
|
|
{
|
|
|
|
list<string> commentLines;
|
|
|
|
ConfigProperty::splitComment(comment, commentLines);
|
|
|
|
for (list<string>::const_iterator line = commentLines.begin();
|
|
|
|
line != commentLines.end();
|
|
|
|
++line) {
|
|
|
|
stream << prefix << *line << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncEvolutionCmdline::usage(bool full, const string &error, const string ¶m)
|
|
|
|
{
|
2008-03-29 18:04:26 +01:00
|
|
|
ostream &out(error.empty() ? m_out : m_err);
|
|
|
|
|
|
|
|
out << m_argv[0] << endl;
|
|
|
|
out << m_argv[0] << " [<options>] <server> [<source> ...]" << endl;
|
|
|
|
out << m_argv[0] << " --help|-h" << endl;
|
|
|
|
out << m_argv[0] << " --version" << endl;
|
2008-03-19 12:29:14 +01:00
|
|
|
if (full) {
|
2008-03-29 18:04:26 +01:00
|
|
|
out << endl <<
|
2008-03-19 12:29:14 +01:00
|
|
|
"Options:" << endl <<
|
|
|
|
" --sync|-s <mode>" << endl <<
|
|
|
|
" Temporarily synchronize the active sources in that mode. Useful" << endl <<
|
|
|
|
" for a 'refresh-from-server' or 'refresh-from-client' sync which" << endl <<
|
|
|
|
" clears all data at one end and copies all items from the other." << endl <<
|
|
|
|
" " << endl <<
|
|
|
|
" --status|-t" << endl <<
|
|
|
|
" The changes made to local data since the last synchronization are" << endl <<
|
|
|
|
" shown without starting a new one. This can be used to see in advance" << endl <<
|
|
|
|
" whether the local data needs to be synchronized with the server." << endl <<
|
|
|
|
" " << endl <<
|
|
|
|
" --quiet|-q" << endl <<
|
|
|
|
" Suppresses most of the normal output during a synchronization. The" << endl <<
|
|
|
|
" log file still contains all the information." << endl <<
|
|
|
|
" " << endl <<
|
|
|
|
" --help|-h" << endl <<
|
|
|
|
" Prints usage information." << endl <<
|
|
|
|
" " << endl <<
|
|
|
|
" --version" << endl <<
|
|
|
|
" Prints the SyncEvolution version." << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error != "") {
|
2008-03-29 18:04:26 +01:00
|
|
|
out << endl << "ERROR: " << error << endl;
|
2008-03-19 12:29:14 +01:00
|
|
|
}
|
|
|
|
if (param != "") {
|
2008-03-29 18:04:26 +01:00
|
|
|
out << "INFO: use '" << param << (param[param.size() - 1] == '=' ? "" : " ") <<
|
2008-03-19 12:29:14 +01:00
|
|
|
"?' to get a list of valid parameters" << endl;
|
|
|
|
}
|
|
|
|
}
|
2008-03-28 23:32:00 +01:00
|
|
|
|
|
|
|
#ifdef ENABLE_UNIT_TESTS
|
|
|
|
|
2008-03-29 18:04:26 +01:00
|
|
|
/** simple line-by-line diff */
|
|
|
|
static string diffStrings(const string &lhs, const string &rhs)
|
|
|
|
{
|
|
|
|
ostringstream res;
|
|
|
|
|
|
|
|
typedef boost::split_iterator<string::const_iterator> string_split_iterator;
|
|
|
|
string_split_iterator lit =
|
|
|
|
boost::make_split_iterator(lhs, boost::first_finder("\n", boost::is_iequal()));
|
|
|
|
string_split_iterator rit =
|
|
|
|
boost::make_split_iterator(rhs, boost::first_finder("\n", boost::is_iequal()));
|
|
|
|
while (lit != string_split_iterator() &&
|
|
|
|
rit != string_split_iterator()) {
|
|
|
|
if (*lit != *rit) {
|
|
|
|
res << "< " << *lit << endl;
|
|
|
|
res << "> " << *rit << endl;
|
|
|
|
}
|
|
|
|
++lit;
|
|
|
|
++rit;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (lit != string_split_iterator()) {
|
|
|
|
res << "< " << *lit << endl;
|
|
|
|
++lit;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (lit != string_split_iterator()) {
|
|
|
|
res << "> " << *rit << endl;
|
|
|
|
++rit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
# define CPPUNIT_ASSERT_EQUAL_DIFF( expected, actual ) \
|
|
|
|
do { \
|
|
|
|
if (!CPPUNIT_NS::assertion_traits<string>::equal(expected,actual)) { \
|
|
|
|
CPPUNIT_NS::Message cpputMsg_(string("expected:\n") + \
|
|
|
|
expected); \
|
|
|
|
cpputMsg_.addDetail(string("actual:\n") + \
|
|
|
|
actual); \
|
|
|
|
cpputMsg_.addDetail(string("diff:\n") + \
|
|
|
|
diffStrings(expected, actual)); \
|
|
|
|
CPPUNIT_NS::Asserter::fail( cpputMsg_, \
|
|
|
|
CPPUNIT_SOURCELINE() ); \
|
|
|
|
} \
|
|
|
|
} while ( false )
|
|
|
|
|
|
|
|
|
2008-03-28 23:32:00 +01:00
|
|
|
/**
|
|
|
|
* Testing is based on a text representation of a directory
|
|
|
|
* hierarchy where each line is of the format
|
|
|
|
* <file path>:<line in file>
|
|
|
|
*
|
|
|
|
* The order of files is alphabetical, of lines in the file as
|
|
|
|
* in the file. Lines in the file without line break cannot
|
|
|
|
* be represented.
|
|
|
|
*
|
|
|
|
* The root of the hierarchy is not part of the representation
|
|
|
|
* itself.
|
|
|
|
*/
|
|
|
|
class SyncEvolutionCmdlineTest : public CppUnit::TestFixture {
|
|
|
|
CPPUNIT_TEST_SUITE(SyncEvolutionCmdlineTest);
|
|
|
|
CPPUNIT_TEST(testFramework);
|
2008-03-29 15:16:34 +01:00
|
|
|
CPPUNIT_TEST(testSetupScheduleWorld);
|
2008-03-29 18:04:26 +01:00
|
|
|
CPPUNIT_TEST(testSetupFunambol);
|
|
|
|
CPPUNIT_TEST(testSetupSynthesis);
|
|
|
|
CPPUNIT_TEST(testTemplate);
|
|
|
|
|
2008-03-28 23:32:00 +01:00
|
|
|
CPPUNIT_TEST_SUITE_END();
|
2008-03-29 15:16:34 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
SyncEvolutionCmdlineTest() :
|
|
|
|
m_testDir("SyncEvolutionCmdlineTest"),
|
|
|
|
m_scheduleWorldConfig(".internal.ini:serverNonce = \n"
|
|
|
|
".internal.ini:clientNonce = \n"
|
|
|
|
".internal.ini:devInfoHash = \n"
|
|
|
|
"config.ini:syncURL = http://sync.scheduleworld.com\n"
|
|
|
|
"config.ini:deviceId = \n"
|
|
|
|
"config.ini:username = your SyncML server account name\n"
|
|
|
|
"config.ini:password = your SyncML server password\n"
|
|
|
|
"config.ini:logdir = \n"
|
|
|
|
"config.ini:loglevel = 0\n"
|
|
|
|
"config.ini:maxlogdirs = 0\n"
|
|
|
|
"config.ini:useProxy = F\n"
|
|
|
|
"config.ini:proxyHost = \n"
|
|
|
|
"config.ini:proxyUsername = \n"
|
|
|
|
"config.ini:proxyPassword = \n"
|
|
|
|
"config.ini:clientAuthType = md5\n"
|
|
|
|
"config.ini:maxMsgSize = 8192\n"
|
|
|
|
"config.ini:maxObjSize = 500000\n"
|
|
|
|
"config.ini:loSupport = T\n"
|
|
|
|
"config.ini:enableCompression = F\n"
|
|
|
|
"addressbook/.internal.ini:last = \n"
|
|
|
|
"addressbook/config.ini:sync = two-way\n"
|
|
|
|
"addressbook/config.ini:type = addressbook\n"
|
|
|
|
"addressbook/config.ini:evolutionsource = \n"
|
|
|
|
"addressbook/config.ini:uri = card3\n"
|
|
|
|
"addressbook/config.ini:evolutionuser = \n"
|
|
|
|
"addressbook/config.ini:evolutionpassword = \n"
|
|
|
|
"addressbook/config.ini:encoding = \n"
|
|
|
|
"calendar/.internal.ini:last = \n"
|
|
|
|
"calendar/config.ini:sync = two-way\n"
|
|
|
|
"calendar/config.ini:type = calendar\n"
|
|
|
|
"calendar/config.ini:evolutionsource = \n"
|
|
|
|
"calendar/config.ini:uri = event2\n"
|
|
|
|
"calendar/config.ini:evolutionuser = \n"
|
|
|
|
"calendar/config.ini:evolutionpassword = \n"
|
|
|
|
"calendar/config.ini:encoding = \n"
|
|
|
|
"memo/.internal.ini:last = \n"
|
|
|
|
"memo/config.ini:sync = two-way\n"
|
|
|
|
"memo/config.ini:type = memo\n"
|
|
|
|
"memo/config.ini:evolutionsource = \n"
|
|
|
|
"memo/config.ini:uri = note\n"
|
|
|
|
"memo/config.ini:evolutionuser = \n"
|
|
|
|
"memo/config.ini:evolutionpassword = \n"
|
|
|
|
"memo/config.ini:encoding = \n"
|
|
|
|
"todo/.internal.ini:last = \n"
|
|
|
|
"todo/config.ini:sync = two-way\n"
|
|
|
|
"todo/config.ini:type = todo\n"
|
|
|
|
"todo/config.ini:evolutionsource = \n"
|
|
|
|
"todo/config.ini:uri = task2\n"
|
|
|
|
"todo/config.ini:evolutionuser = \n"
|
|
|
|
"todo/config.ini:evolutionpassword = \n"
|
|
|
|
"todo/config.ini:encoding = \n")
|
|
|
|
{}
|
2008-03-28 23:32:00 +01:00
|
|
|
|
|
|
|
protected:
|
|
|
|
|
2008-03-29 15:16:34 +01:00
|
|
|
/** verify that createFiles/scanFiles themselves work */
|
2008-03-28 23:32:00 +01:00
|
|
|
void testFramework() {
|
2008-03-29 15:16:34 +01:00
|
|
|
const string root(m_testDir);
|
2008-03-28 23:32:00 +01:00
|
|
|
const string content("baz:line\n"
|
2008-03-29 15:16:34 +01:00
|
|
|
"caz/subdir:booh\n"
|
2008-03-28 23:32:00 +01:00
|
|
|
"foo:bar1\n"
|
|
|
|
"foo:bar2\n");
|
|
|
|
createFiles(root, content);
|
|
|
|
string res = scanFiles(root);
|
2008-03-29 18:04:26 +01:00
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF(content, res);
|
2008-03-28 23:32:00 +01:00
|
|
|
}
|
|
|
|
|
2008-03-29 18:04:26 +01:00
|
|
|
/** create new configurations */
|
2008-03-29 15:16:34 +01:00
|
|
|
void testSetupScheduleWorld() {
|
2008-03-29 18:04:26 +01:00
|
|
|
string root;
|
2008-03-29 15:16:34 +01:00
|
|
|
ScopedEnvChange xdg("XDG_CONFIG_HOME", m_testDir);
|
|
|
|
ScopedEnvChange home("HOME", m_testDir);
|
2008-03-29 18:04:26 +01:00
|
|
|
|
|
|
|
root = m_testDir;
|
|
|
|
root += "/syncevolution/scheduleworld";
|
|
|
|
rm_r(root);
|
2008-03-29 15:16:34 +01:00
|
|
|
TestCmdline cmdline("--configure",
|
|
|
|
"--template", "scheduleworld",
|
|
|
|
"scheduleworld",
|
|
|
|
NULL);
|
|
|
|
cmdline.doit();
|
|
|
|
string res = scanFiles(root);
|
2008-03-29 18:04:26 +01:00
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF(string(m_scheduleWorldConfig), res);
|
2008-03-29 15:16:34 +01:00
|
|
|
}
|
2008-03-29 18:04:26 +01:00
|
|
|
void testSetupFunambol() {
|
|
|
|
string root;
|
|
|
|
ScopedEnvChange xdg("XDG_CONFIG_HOME", m_testDir);
|
|
|
|
ScopedEnvChange home("HOME", m_testDir);
|
|
|
|
|
|
|
|
root = m_testDir;
|
|
|
|
root += "/syncevolution/funambol";
|
|
|
|
rm_r(root);
|
|
|
|
TestCmdline cmdline("--configure",
|
|
|
|
"--template", "funambol",
|
|
|
|
"funambol",
|
|
|
|
NULL);
|
|
|
|
cmdline.doit();
|
|
|
|
string res = scanFiles(root);
|
|
|
|
string expected = m_scheduleWorldConfig;
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"syncURL = http://sync.scheduleworld.com",
|
|
|
|
"syncURL = http://my.funambol.com");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"addressbook/config.ini:uri = card3",
|
|
|
|
"addressbook/config.ini:uri = card");
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"addressbook/config.ini:type = addressbook",
|
|
|
|
"addressbook/config.ini:type = addressbook:text/x-vcard");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"calendar/config.ini:uri = event2",
|
|
|
|
"calendar/config.ini:uri = event");
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"calendar/config.ini:sync = two-way",
|
|
|
|
"calendar/config.ini:sync = disabled");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"memo/config.ini:sync = two-way",
|
|
|
|
"memo/config.ini:sync = disabled");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"todo/config.ini:uri = task2",
|
|
|
|
"todo/config.ini:uri = task");
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"todo/config.ini:sync = two-way",
|
|
|
|
"todo/config.ini:sync = disabled");
|
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF(expected, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testSetupSynthesis() {
|
|
|
|
string root;
|
|
|
|
ScopedEnvChange xdg("XDG_CONFIG_HOME", m_testDir);
|
|
|
|
ScopedEnvChange home("HOME", m_testDir);
|
|
|
|
|
|
|
|
root = m_testDir;
|
|
|
|
root += "/syncevolution/synthesis";
|
|
|
|
rm_r(root);
|
|
|
|
TestCmdline cmdline("--configure",
|
|
|
|
"--template", "synthesis",
|
|
|
|
"synthesis",
|
|
|
|
NULL);
|
|
|
|
cmdline.doit();
|
|
|
|
string res = scanFiles(root);
|
|
|
|
string expected = m_scheduleWorldConfig;
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"syncURL = http://sync.scheduleworld.com",
|
|
|
|
"syncURL = http://www.synthesis.ch/sync");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"addressbook/config.ini:uri = card3",
|
|
|
|
"addressbook/config.ini:uri = contacts");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"calendar/config.ini:uri = event2",
|
|
|
|
"calendar/config.ini:uri = events");
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"calendar/config.ini:sync = two-way",
|
|
|
|
"calendar/config.ini:sync = disabled");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"memo/config.ini:uri = note",
|
|
|
|
"memo/config.ini:uri = notes");
|
|
|
|
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"todo/config.ini:uri = task2",
|
|
|
|
"todo/config.ini:uri = tasks");
|
|
|
|
boost::replace_first(expected,
|
|
|
|
"todo/config.ini:sync = two-way",
|
|
|
|
"todo/config.ini:sync = disabled");
|
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF(expected, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testTemplate() {
|
|
|
|
TestCmdline failure("--template", NULL);
|
|
|
|
CPPUNIT_ASSERT(!failure.m_cmdline->parse());
|
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF("", failure.m_out.str());
|
|
|
|
CPPUNIT_ASSERT(boost::ends_with(failure.m_err.str(), "ERROR: missing parameter for '--template'\n"));
|
|
|
|
|
|
|
|
TestCmdline help("--template", "?", NULL);
|
|
|
|
help.doit();
|
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF("Available configuration templates:\n"
|
|
|
|
" funambol = http://my.funambol.com\n"
|
|
|
|
" scheduleworld = http://sync.scheduleworld.com\n"
|
|
|
|
" synthesis = http://www.synthesis.ch\n",
|
|
|
|
help.m_out.str());
|
|
|
|
CPPUNIT_ASSERT_EQUAL_DIFF("", help.m_err.str());
|
|
|
|
}
|
|
|
|
|
2008-03-29 15:16:34 +01:00
|
|
|
|
|
|
|
const string m_testDir;
|
|
|
|
const string m_scheduleWorldConfig;
|
|
|
|
|
|
|
|
|
2008-03-28 23:32:00 +01:00
|
|
|
private:
|
|
|
|
|
2008-03-29 15:16:34 +01:00
|
|
|
/**
|
|
|
|
* vararg constructor with NULL termination,
|
|
|
|
* out and error stream into stringstream members
|
|
|
|
*/
|
|
|
|
class TestCmdline {
|
|
|
|
public:
|
|
|
|
TestCmdline(const char *arg, ...) {
|
|
|
|
va_list argList;
|
|
|
|
va_start (argList, arg);
|
|
|
|
for (const char *curr = arg;
|
|
|
|
curr;
|
|
|
|
curr = va_arg(argList, const char *)) {
|
|
|
|
m_argvstr.push_back(curr);
|
|
|
|
}
|
|
|
|
va_end(argList);
|
|
|
|
|
|
|
|
m_argv.reset(new const char *[m_argvstr.size() + 1]);
|
|
|
|
m_argv[0] = "client-test";
|
|
|
|
for (size_t index = 0;
|
|
|
|
index < m_argvstr.size();
|
|
|
|
++index) {
|
|
|
|
m_argv[index + 1] = m_argvstr[index].c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_cmdline.set(new SyncEvolutionCmdline(m_argvstr.size() + 1, m_argv.get(), m_out, m_err), "cmdline");
|
|
|
|
}
|
|
|
|
|
|
|
|
void doit() {
|
|
|
|
bool success;
|
|
|
|
success = m_cmdline->parse() &&
|
|
|
|
m_cmdline->run();
|
|
|
|
if (m_err.str().size()) {
|
|
|
|
cerr << endl << m_err.str();
|
|
|
|
}
|
|
|
|
CPPUNIT_ASSERT(success);
|
|
|
|
}
|
|
|
|
|
|
|
|
ostringstream m_out, m_err;
|
|
|
|
cxxptr<SyncEvolutionCmdline> m_cmdline;
|
|
|
|
|
|
|
|
private:
|
|
|
|
vector<string> m_argvstr;
|
|
|
|
boost::scoped_array<const char *> m_argv;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** temporarily set env variable, restore old value on destruction */
|
|
|
|
class ScopedEnvChange {
|
|
|
|
public:
|
|
|
|
ScopedEnvChange(const string &var, const string &value) :
|
|
|
|
m_var(var)
|
|
|
|
{
|
|
|
|
const char *oldval = getenv(var.c_str());
|
|
|
|
if (oldval) {
|
|
|
|
m_oldvalset = true;
|
|
|
|
m_oldval = oldval;
|
|
|
|
} else {
|
|
|
|
m_oldvalset = false;
|
|
|
|
}
|
|
|
|
setenv(var.c_str(), value.c_str(), 1);
|
|
|
|
}
|
|
|
|
~ScopedEnvChange()
|
|
|
|
{
|
|
|
|
if (m_oldvalset) {
|
|
|
|
setenv(m_var.c_str(), m_oldval.c_str(), 1);
|
|
|
|
} else {
|
|
|
|
unsetenv(m_var.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
string m_var, m_oldval;
|
|
|
|
bool m_oldvalset;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-03-28 23:32:00 +01:00
|
|
|
/** create directory hierarchy, overwriting previous content */
|
|
|
|
void createFiles(const string &root, const string &content) {
|
|
|
|
rm_r(root);
|
|
|
|
|
|
|
|
size_t start = 0;
|
|
|
|
ofstream out;
|
|
|
|
string outname;
|
|
|
|
|
|
|
|
out.exceptions(ios_base::badbit|ios_base::failbit);
|
|
|
|
while (start < content.size()) {
|
|
|
|
size_t delim = content.find(':', start);
|
|
|
|
size_t end = content.find('\n', start);
|
|
|
|
if (delim == content.npos ||
|
|
|
|
end == content.npos) {
|
|
|
|
// invalid content ?!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
string newname = content.substr(start, delim - start);
|
|
|
|
string line = content.substr(delim + 1, end - delim - 1);
|
|
|
|
if (newname != outname) {
|
|
|
|
if (out.is_open()) {
|
|
|
|
out.close();
|
|
|
|
}
|
|
|
|
string fullpath = root + "/" + newname;
|
|
|
|
size_t fileoff = fullpath.rfind('/');
|
|
|
|
mkdir_p(fullpath.substr(0, fileoff));
|
|
|
|
out.open(fullpath.c_str());
|
|
|
|
outname = newname;
|
|
|
|
}
|
|
|
|
out << line << endl;
|
|
|
|
start = end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** turn directory hierarchy into string */
|
2008-03-29 15:16:34 +01:00
|
|
|
string scanFiles(const string &root, bool onlyProps = true) {
|
2008-03-28 23:32:00 +01:00
|
|
|
ostringstream out;
|
|
|
|
|
2008-03-29 15:16:34 +01:00
|
|
|
scanFiles(root, "", out, onlyProps);
|
2008-03-28 23:32:00 +01:00
|
|
|
return out.str();
|
|
|
|
}
|
2008-03-29 15:16:34 +01:00
|
|
|
void scanFiles(const string &root, const string &dir, ostringstream &out, bool onlyProps) {
|
|
|
|
string newroot = root;
|
|
|
|
newroot += "/";
|
|
|
|
newroot += dir;
|
|
|
|
ReadDir readDir(newroot);
|
2008-03-28 23:32:00 +01:00
|
|
|
sort(readDir.begin(), readDir.end());
|
|
|
|
|
|
|
|
for (ReadDir::const_iterator it = readDir.begin();
|
|
|
|
it != readDir.end();
|
|
|
|
++it) {
|
2008-03-29 15:16:34 +01:00
|
|
|
if (isDir(newroot + "/" + *it)) {
|
|
|
|
scanFiles(newroot, *it, out, onlyProps);
|
|
|
|
} else {
|
|
|
|
ifstream in;
|
|
|
|
in.exceptions(ios_base::badbit /* failbit must not trigger exception because is set when reaching eof ?! */);
|
|
|
|
in.open((newroot + "/" + *it).c_str());
|
|
|
|
string line;
|
|
|
|
while (!in.eof()) {
|
|
|
|
getline(in, line);
|
|
|
|
if ((line.size() || !in.eof()) &&
|
|
|
|
(!onlyProps ||
|
|
|
|
line.size() && line[0] != '#')) {
|
|
|
|
if (dir.size()) {
|
|
|
|
out << dir << "/";
|
|
|
|
}
|
|
|
|
out << *it << ":";
|
|
|
|
out << line << '\n';
|
2008-03-28 23:32:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
SYNCEVOLUTION_TEST_SUITE_REGISTRATION(SyncEvolutionCmdlineTest);
|
|
|
|
|
|
|
|
#endif // ENABLE_UNIT_TESTS
|