gnucash master: Multiple changes pushed
John Ralls
jralls at code.gnucash.org
Mon Nov 28 15:27:47 EST 2016
Updated via https://github.com/Gnucash/gnucash/commit/e592e00e (commit)
via https://github.com/Gnucash/gnucash/commit/abb66016 (commit)
via https://github.com/Gnucash/gnucash/commit/06af7d79 (commit)
via https://github.com/Gnucash/gnucash/commit/46ce3f37 (commit)
via https://github.com/Gnucash/gnucash/commit/eace6250 (commit)
via https://github.com/Gnucash/gnucash/commit/90a95204 (commit)
via https://github.com/Gnucash/gnucash/commit/52683403 (commit)
from https://github.com/Gnucash/gnucash/commit/dd4b8a10 (commit)
commit e592e00e10bd1cee31de0e21c68ce66e6069b97c
Merge: dd4b8a1 abb6601
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Nov 28 12:26:26 2016 -0800
Third interim merge of 'c++-backend'
commit abb66016bc8ef1b2d5c5eaafa7020bfe48d55bb6
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Nov 28 10:27:09 2016 -0800
Convert QofBackend to a C++ class and the backend class hierarchy into C++.
Getting rid of all of the casting and different flavors of backend pointers
and adopting the C++ member-function calling conventions.
diff --git a/src/backend/dbi/gnc-backend-dbi.cpp b/src/backend/dbi/gnc-backend-dbi.cpp
index cbd251e..5e00451 100644
--- a/src/backend/dbi/gnc-backend-dbi.cpp
+++ b/src/backend/dbi/gnc-backend-dbi.cpp
@@ -111,31 +111,8 @@ static QofLogModule log_module = G_LOG_DOMAIN;
static void adjust_sql_options (dbi_conn connection);
static bool save_may_clobber_data (dbi_conn conn, const std::string& dbname);
-static void init_sql_backend (GncDbiBackend* dbi_be);
-static bool conn_test_dbi_library (dbi_conn conn, QofBackend* qof_be);
-
-template <DbType T> void gnc_dbi_session_begin(QofBackend* qof_be,
- QofSession* session,
- const char* book_id,
- gboolean ignore_lock,
- gboolean create, gboolean force);
-template <DbType Type> QofBackend*
-new_backend ()
-{
- auto dbi_be = new GncDbiBackend(nullptr, nullptr);
- assert (dbi_be != nullptr);
-
- QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(dbi_be);
- qof_backend_init (qof_be);
-
- qof_be->session_begin = gnc_dbi_session_begin<Type>;
- init_sql_backend (dbi_be);
-
- return qof_be;
-}
-
-template <DbType T>
+template <DbType Type>
class QofDbiBackendProvider : public QofBackendProvider
{
public:
@@ -146,7 +123,10 @@ public:
QofDbiBackendProvider(QofDbiBackendProvider&&) = delete;
QofDbiBackendProvider operator=(QofDbiBackendProvider&&) = delete;
~QofDbiBackendProvider () = default;
- QofBackend* create_backend(void) { return new_backend<T>(); }
+ QofBackend* create_backend(void)
+ {
+ return new GncDbiBackend<Type>(nullptr, nullptr);
+ }
bool type_check(const char* type) { return true; }
};
@@ -235,14 +215,13 @@ set_options(dbi_conn conn, const PairVec& options)
/**
* Sets standard db options in a dbi_conn.
*
- * @param qof_be QOF backend
* @param conn dbi_conn connection
* @param uri UriStrings containing the needed paramters.
* @return TRUE if successful, FALSE if error
*/
-static bool
-set_standard_connection_options (QofBackend* qof_be, dbi_conn conn,
- const UriStrings& uri)
+template <DbType Type> bool
+GncDbiBackend<Type>::set_standard_connection_options (dbi_conn conn,
+ const UriStrings& uri)
{
gint result;
@@ -266,7 +245,7 @@ set_standard_connection_options (QofBackend* qof_be, dbi_conn conn,
}
catch (std::runtime_error& err)
{
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
return false;
}
@@ -277,8 +256,7 @@ template <DbType Type> void error_handler(void* conn, void* data);
void error_handler(void* conn, void* data);
template <DbType Type> dbi_conn
-conn_setup (QofBackend* qof_be, PairVec& options,
- UriStrings& uri)
+GncDbiBackend<Type>::conn_setup (PairVec& options, UriStrings& uri)
{
const char* dbstr = (Type == DbType::DBI_SQLITE ? "sqlite3" :
Type == DbType::DBI_MYSQL ? "mysql" : "pgsql");
@@ -295,13 +273,13 @@ conn_setup (QofBackend* qof_be, PairVec& options,
if (conn == nullptr)
{
PERR ("Unable to create %s dbi connection", dbstr);
- qof_backend_set_error (qof_be, ERR_BACKEND_BAD_URL);
+ set_error (ERR_BACKEND_BAD_URL);
return nullptr;
}
- dbi_conn_error_handler (conn, error_handler<Type>, qof_be);
+ dbi_conn_error_handler (conn, error_handler<Type>, this);
if (!uri.m_dbname.empty() &&
- !set_standard_connection_options(qof_be, conn, uri))
+ !set_standard_connection_options(conn, uri))
{
dbi_conn_close(conn);
return nullptr;
@@ -314,7 +292,7 @@ conn_setup (QofBackend* qof_be, PairVec& options,
catch (std::runtime_error& err)
{
dbi_conn_close(conn);
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
return nullptr;
}
}
@@ -322,12 +300,12 @@ conn_setup (QofBackend* qof_be, PairVec& options,
return conn;
}
-static bool
-create_database(DbType type, QofBackend *qof_be, dbi_conn conn, const char* db)
+template <DbType Type>bool
+GncDbiBackend<Type>::create_database(dbi_conn conn, const char* db)
{
const char *dbname;
const char *dbcreate;
- if (type == DbType::DBI_MYSQL)
+ if (Type == DbType::DBI_MYSQL)
{
dbname = "mysql";
dbcreate = "CREATE DATABASE %s CHARACTER SET utf8";
@@ -345,7 +323,7 @@ create_database(DbType type, QofBackend *qof_be, dbi_conn conn, const char* db)
}
catch (std::runtime_error& err)
{
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
return false;
}
@@ -353,19 +331,19 @@ create_database(DbType type, QofBackend *qof_be, dbi_conn conn, const char* db)
if (result < 0)
{
PERR ("Unable to connect to %s database", dbname);
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error(ERR_BACKEND_SERVER_ERR);
return false;
}
- if (type == DbType::DBI_MYSQL)
+ if (Type == DbType::DBI_MYSQL)
adjust_sql_options(conn);
auto dresult = dbi_conn_queryf (conn, dbcreate, db);
if (dresult == nullptr)
{
PERR ("Unable to create database '%s'\n", db);
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
return false;
}
- if (type == DbType::DBI_PGSQL)
+ if (Type == DbType::DBI_PGSQL)
{
const char *alterdb = "ALTER DATABASE %s SET "
"standard_conforming_strings TO on";
@@ -380,26 +358,23 @@ template <> void
error_handler<DbType::DBI_SQLITE> (dbi_conn conn, void* user_data)
{
const char* msg;
- GncDbiBackend *dbi_be = static_cast<decltype(dbi_be)>(user_data);
+ GncDbiBackend<DbType::DBI_SQLITE> *dbi_be =
+ static_cast<decltype(dbi_be)>(user_data);
int errnum = dbi_conn_error (conn, &msg);
PERR ("DBI error: %s\n", msg);
if (dbi_be->connected())
- dbi_be->set_error (ERR_BACKEND_MISC, 0, false);
+ dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, false);
}
template <> void
-gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
- QofSession* session,
- const char* book_id,
- gboolean ignore_lock,
- gboolean create, gboolean force)
+GncDbiBackend<DbType::DBI_SQLITE>::session_begin(QofSession* session,
+ const char* book_id,
+ bool ignore_lock,
+ bool create, bool force)
{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
- const char* msg = nullptr;
gboolean file_exists;
PairVec options;
- g_return_if_fail (qof_be != nullptr);
g_return_if_fail (session != nullptr);
g_return_if_fail (book_id != nullptr);
@@ -414,9 +389,9 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
file_exists = g_file_test (filepath.c_str(), ftest);
if (!create && !file_exists)
{
- qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (qof_be, "Sqlite3 file %s not found",
- filepath.c_str());
+ set_error (ERR_FILEIO_FILE_NOT_FOUND);
+ std::string msg{"Sqlite3 file "};
+ set_message (msg + filepath + " not found");
PWARN ("Sqlite3 file %s not found", filepath.c_str());
LEAVE("Error");
return;
@@ -424,14 +399,14 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
if (create && !force && file_exists)
{
- qof_backend_set_error (qof_be, ERR_BACKEND_STORE_EXISTS);
- msg = "Might clobber, no force";
+ set_error (ERR_BACKEND_STORE_EXISTS);
+ auto msg = "Might clobber, no force";
PWARN ("%s", msg);
LEAVE("Error");
return;
}
- dbi_be->connect(nullptr);
+ connect(nullptr);
/* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */
options.push_back(std::make_pair("host", "localhost"));
auto dirname = g_path_get_dirname (filepath.c_str());
@@ -441,7 +416,7 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
if (basename != nullptr) g_free (basename);
if (dirname != nullptr) g_free (dirname);
UriStrings uri;
- auto conn = conn_setup<DbType::DBI_SQLITE>(qof_be, options, uri);
+ auto conn = conn_setup(options, uri);
if (conn == nullptr)
{
LEAVE("Error");
@@ -454,12 +429,12 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
{
dbi_conn_close(conn);
PERR ("Unable to connect to %s: %d\n", book_id, result);
- qof_backend_set_error (qof_be, ERR_BACKEND_BAD_URL);
+ set_error (ERR_BACKEND_BAD_URL);
LEAVE("Error");
return;
}
- if (!conn_test_dbi_library(conn, qof_be))
+ if (!conn_test_dbi_library(conn))
{
if (create && !file_exists)
{
@@ -477,8 +452,8 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
try
{
- dbi_be->connect(new GncDbiSqlConnection(DbType::DBI_SQLITE,
- qof_be, conn, ignore_lock));
+ connect(new GncDbiSqlConnection(DbType::DBI_SQLITE,
+ this, conn, ignore_lock));
}
catch (std::runtime_error& err)
{
@@ -497,7 +472,8 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
template <> void
error_handler<DbType::DBI_MYSQL> (dbi_conn conn, void* user_data)
{
- GncDbiBackend* dbi_be = static_cast<decltype(dbi_be)>(user_data);
+ GncDbiBackend<DbType::DBI_MYSQL>* dbi_be =
+ static_cast<decltype(dbi_be)>(user_data);
const char* msg;
auto err_num = dbi_conn_error (conn, &msg);
@@ -531,18 +507,18 @@ error_handler<DbType::DBI_MYSQL> (dbi_conn conn, void* user_data)
if (err_num == 2006) // Server has gone away
{
PINFO ("DBI error: %s - Reconnecting...\n", msg);
- dbi_be->set_error (ERR_BACKEND_CONN_LOST, 1, true);
+ dbi_be->set_dbi_error (ERR_BACKEND_CONN_LOST, 1, true);
dbi_be->retry_connection(msg);
}
else if (err_num == 2003) // Unable to connect
{
- dbi_be->set_error (ERR_BACKEND_CANT_CONNECT, 1, true);
+ dbi_be->set_dbi_error (ERR_BACKEND_CANT_CONNECT, 1, true);
dbi_be->retry_connection (msg);
}
else // Any other error
{
PERR ("DBI error: %s\n", msg);
- dbi_be->set_error (ERR_BACKEND_MISC, 0, FALSE);
+ dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, FALSE);
}
}
@@ -610,16 +586,13 @@ adjust_sql_options (dbi_conn connection)
}
-template <DbType T> void
-gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
- const char* book_id, gboolean ignore_lock,
- gboolean create, gboolean force)
+template <DbType Type> void
+GncDbiBackend<Type>::session_begin (QofSession* session, const char* book_id,
+ bool ignore_lock, bool create, bool force)
{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
GncDbiTestResult dbi_test_result = GNC_DBI_PASS;
PairVec options;
- g_return_if_fail (qof_be != nullptr);
g_return_if_fail (session != nullptr);
g_return_if_fail (book_id != nullptr);
@@ -630,7 +603,7 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
where username, password and port are optional) */
UriStrings uri(book_id);
- if (T == DbType::DBI_PGSQL)
+ if (Type == DbType::DBI_PGSQL)
{
if (uri.m_portnum == 0)
uri.m_portnum = PGSQL_DEFAULT_PORT;
@@ -642,31 +615,31 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
uri.m_dbname = std::string{lcname};
g_free(lcname);
}
- dbi_be->connect(nullptr);
+ connect(nullptr);
- auto conn = conn_setup<T>(qof_be, options, uri);
+ auto conn = conn_setup(options, uri);
if (conn == nullptr)
{
LEAVE("Error");
return;
}
- dbi_be->set_exists(true); //May be unset in the error handler.
+ m_exists = true; //May be unset in the error handler.
auto result = dbi_conn_connect (conn);
if (result == 0)
{
- if (T == DbType::DBI_MYSQL)
+ if (Type == DbType::DBI_MYSQL)
adjust_sql_options (conn);
- if(!conn_test_dbi_library(conn, qof_be))
+ if(!conn_test_dbi_library(conn))
{
dbi_conn_close(conn);
LEAVE("Error");
return;
}
if (create && !force && save_may_clobber_data (conn,
- uri.quote_dbname(T)))
+ uri.quote_dbname(Type)))
{
- qof_backend_set_error (qof_be, ERR_BACKEND_STORE_EXISTS);
+ set_error (ERR_BACKEND_STORE_EXISTS);
PWARN ("Databse already exists, Might clobber it.");
dbi_conn_close(conn);
LEAVE("Error");
@@ -677,10 +650,10 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
else
{
- if (dbi_be->exists())
+ if (m_exists)
{
PERR ("Unable to connect to database '%s'\n", uri.dbname());
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
dbi_conn_close(conn);
LEAVE("Error");
return;
@@ -688,45 +661,46 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
if (create)
{
- if (!create_database(T, qof_be, conn, uri.quote_dbname(T).c_str()))
+ if (!create_database(conn, uri.quote_dbname(Type).c_str()))
{
dbi_conn_close(conn);
LEAVE("Error");
return;
}
- conn = conn_setup<T>(qof_be, options, uri);
+ conn = conn_setup(options, uri);
result = dbi_conn_connect (conn);
if (result < 0)
{
PERR ("Unable to create database '%s'\n", uri.dbname());
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
dbi_conn_close(conn);
LEAVE("Error");
return;
}
- if (T == DbType::DBI_MYSQL)
+ if (Type == DbType::DBI_MYSQL)
adjust_sql_options (conn);
- if (!conn_test_dbi_library(conn, qof_be))
+ if (!conn_test_dbi_library(conn))
{
- if (T == DbType::DBI_PGSQL)
+ if (Type == DbType::DBI_PGSQL)
dbi_conn_select_db (conn, "template1");
dbi_conn_queryf (conn, "DROP DATABASE %s",
- uri.quote_dbname(T).c_str());
+ uri.quote_dbname(Type).c_str());
dbi_conn_close(conn);
return;
}
}
else
{
- qof_backend_set_error (qof_be, ERR_BACKEND_NO_SUCH_DB);
- qof_backend_set_message (qof_be, "Database %s not found", uri.dbname());
+ set_error(ERR_BACKEND_NO_SUCH_DB);
+ std::string msg{"Database "};
+ set_message(msg + uri.dbname() + " not found");
}
}
- dbi_be->connect(nullptr);
+ connect(nullptr);
try
{
- dbi_be->connect(new GncDbiSqlConnection(T, qof_be, conn, ignore_lock));
+ connect(new GncDbiSqlConnection(Type, this, conn, ignore_lock));
}
catch (std::runtime_error& err)
{
@@ -745,7 +719,8 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
template<> void
error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
{
- GncDbiBackend* dbi_be = static_cast<decltype(dbi_be)>(user_data);
+ GncDbiBackend<DbType::DBI_PGSQL>* dbi_be =
+ static_cast<decltype(dbi_be)>(user_data);
const char* msg;
(void)dbi_conn_error (conn, &msg);
@@ -764,7 +739,7 @@ error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
return;
}
PINFO ("DBI error: %s - Reconnecting...\n", msg);
- dbi_be->set_error (ERR_BACKEND_CONN_LOST, 1, true);
+ dbi_be->set_dbi_error (ERR_BACKEND_CONN_LOST, 1, true);
dbi_be->retry_connection(msg);
}
else if (g_str_has_prefix (msg, "connection pointer is NULL") ||
@@ -776,7 +751,7 @@ error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
ERR_BACKEND_CANT_CONNECT);
else
{
- dbi_be->set_error(ERR_BACKEND_CANT_CONNECT, 1, true);
+ dbi_be->set_dbi_error(ERR_BACKEND_CANT_CONNECT, 1, true);
dbi_be->retry_connection (msg);
}
}
@@ -784,38 +759,28 @@ error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
{
PERR ("DBI error: %s\n", msg);
if (dbi_be->connected())
- dbi_be->set_error (ERR_BACKEND_MISC, 0, false);
+ dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, false);
}
}
/* ================================================================= */
-static void
-gnc_dbi_session_end (QofBackend* qof_be)
+template <DbType Type> void
+GncDbiBackend<Type>::session_end ()
{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
-
- g_return_if_fail (dbi_be != nullptr);
-
ENTER (" ");
- dbi_be->finalize_version_info ();
- dbi_be->connect(nullptr);
+ finalize_version_info ();
+ connect(nullptr);
LEAVE (" ");
}
-static void
-gnc_dbi_destroy_backend (QofBackend* qof_be)
+template <DbType Type>
+GncDbiBackend<Type>::~GncDbiBackend()
{
- g_return_if_fail (qof_be != nullptr);
-
/* Stop transaction logging */
xaccLogSetBaseName (nullptr);
-
- qof_backend_destroy (qof_be);
-
- g_free (qof_be);
}
/* ================================================================= */
@@ -829,41 +794,38 @@ gnc_dbi_destroy_backend (QofBackend* qof_be)
* then the database will be loaded read-only. A resave will update
* both values to match this version of Gnucash.
*/
-void
-gnc_dbi_load (QofBackend* qof_be, QofBook* book, QofBackendLoadType loadType)
+template <DbType Type> void
+GncDbiBackend<Type>::load (QofBook* book, QofBackendLoadType loadType)
{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
-
- g_return_if_fail (qof_be != nullptr);
g_return_if_fail (book != nullptr);
- ENTER ("dbi_be=%p, book=%p", dbi_be, book);
+ ENTER ("dbi_be=%p, book=%p", this, book);
if (loadType == LOAD_TYPE_INITIAL_LOAD)
{
// Set up table version information
- dbi_be->init_version_info ();
- assert (dbi_be->m_book == nullptr);
- dbi_be->create_tables();
+ init_version_info ();
+ assert (m_book == nullptr);
+ create_tables();
}
- dbi_be->load(book, loadType);
+ GncSqlBackend::load(book, loadType);
- if (GNUCASH_RESAVE_VERSION > dbi_be->get_table_version("Gnucash"))
+ if (GNUCASH_RESAVE_VERSION > get_table_version("Gnucash"))
{
/* The database was loaded with an older database schema or
* data semantics. In order to ensure consistency, the whole
* thing needs to be saved anew. */
- qof_backend_set_error (qof_be, ERR_SQL_DB_TOO_OLD);
+ set_error(ERR_SQL_DB_TOO_OLD);
}
- else if (GNUCASH_RESAVE_VERSION < dbi_be->get_table_version("Gnucash-Resave"))
+ else if (GNUCASH_RESAVE_VERSION < get_table_version("Gnucash-Resave"))
{
/* Worse, the database was created with a newer version. We
* can't safely write to this database, so the user will have
* to do a "save as" to make one that we can write to.
*/
- qof_backend_set_error (qof_be, ERR_SQL_DB_TOO_NEW);
+ set_error(ERR_SQL_DB_TOO_NEW);
}
@@ -894,17 +856,14 @@ save_may_clobber_data (dbi_conn conn, const std::string& dbname)
* no errors. If there are errors, drop the new tables and restore the
* originals.
*
- * @param qof_be: QofBackend for the session.
* @param book: QofBook to be saved in the database.
*/
-void
-gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
+template <DbType Type> void
+GncDbiBackend<Type>::safe_sync (QofBook* book)
{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
- auto conn = dynamic_cast<GncDbiSqlConnection*>(dbi_be->m_conn);
+ auto conn = dynamic_cast<GncDbiSqlConnection*>(m_conn);
g_return_if_fail (conn != nullptr);
- g_return_if_fail (dbi_be != nullptr);
g_return_if_fail (book != nullptr);
ENTER ("book=%p, primary=%p", book, m_book);
@@ -921,12 +880,11 @@ gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
set_error (ERR_BACKEND_SERVER_ERR);
set_message("Failed to drop indexes");
LEAVE ("Failed to drop indexes");
- return;
- }
+ return;
}
- dbi_be->sync_all(book);
- if (qof_backend_check_error (qof_be))
+ sync(m_book);
+ if (check_error())
{
conn->table_operation (TableOpType::rollback);
LEAVE ("Failed to create new database tables");
@@ -936,63 +894,6 @@ gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
LEAVE ("book=%p", m_book);
}
/* ================================================================= */
-static void
-gnc_dbi_begin_edit (QofBackend* qof_be, QofInstance* inst)
-{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
-
- g_return_if_fail (dbi_be != nullptr);
- g_return_if_fail (inst != nullptr);
-
- dbi_be->begin_edit(inst);
-}
-
-static void
-gnc_dbi_rollback_edit (QofBackend* qof_be, QofInstance* inst)
-{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
-
- g_return_if_fail (dbi_be != nullptr);
- g_return_if_fail (inst != nullptr);
-
- dbi_be->rollback_edit(inst);
-}
-
-static void
-gnc_dbi_commit_edit (QofBackend* qof_be, QofInstance* inst)
-{
- GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
-
- g_return_if_fail (dbi_be != nullptr);
- g_return_if_fail (inst != nullptr);
-
- dbi_be->commit_edit(inst);
-}
-
-/* ================================================================= */
-
-static void
-init_sql_backend (GncDbiBackend* dbi_be)
-{
- QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(dbi_be);
-
- qof_be->session_end = gnc_dbi_session_end;
- qof_be->destroy_backend = gnc_dbi_destroy_backend;
-
- qof_be->load = gnc_dbi_load;
-
- /* The gda backend treats accounting periods transactionally. */
- qof_be->begin = gnc_dbi_begin_edit;
- qof_be->commit = gnc_dbi_commit_edit;
- qof_be->rollback = gnc_dbi_rollback_edit;
-
- /* The SQL/DBI backend doesn't need to be synced until it is
- * configured for multiuser access. */
- qof_be->sync = gnc_dbi_safe_sync_all;
- qof_be->safe_sync = gnc_dbi_safe_sync_all;
- /* CoA Export function not implemented for the SQL backend. */
- qof_be->export_fn = nullptr;
-}
/*
* Checks to see whether the file is an sqlite file or not
@@ -1254,8 +1155,8 @@ dbi_library_test (dbi_conn conn)
return retval;
}
-static bool
-conn_test_dbi_library(dbi_conn conn, QofBackend* qof_be)
+template <DbType Type> bool
+GncDbiBackend<Type>::conn_test_dbi_library(dbi_conn conn)
{
auto result = dbi_library_test (conn);
switch (result)
@@ -1264,15 +1165,13 @@ conn_test_dbi_library(dbi_conn conn, QofBackend* qof_be)
break;
case GNC_DBI_FAIL_SETUP:
- qof_backend_set_error (qof_be, ERR_SQL_DBI_UNTESTABLE);
- qof_backend_set_message (qof_be,
- "DBI library large number test incomplete");
+ set_error(ERR_SQL_DBI_UNTESTABLE);
+ set_message ("DBI library large number test incomplete");
break;
case GNC_DBI_FAIL_TEST:
- qof_backend_set_error (qof_be, ERR_SQL_BAD_DBI);
- qof_backend_set_message (qof_be,
- "DBI library fails large number test");
+ set_error (ERR_SQL_BAD_DBI);
+ set_message ("DBI library fails large number test");
break;
}
return result == GNC_DBI_PASS;
diff --git a/src/backend/dbi/gnc-backend-dbi.hpp b/src/backend/dbi/gnc-backend-dbi.hpp
index 796c825..5d02732 100644
--- a/src/backend/dbi/gnc-backend-dbi.hpp
+++ b/src/backend/dbi/gnc-backend-dbi.hpp
@@ -84,14 +84,22 @@ enum class DbType
/**
* Implementations of GncSqlBackend.
*/
+struct UriStrings;
+
+template <DbType Type>
class GncDbiBackend : public GncSqlBackend
{
public:
GncDbiBackend(GncSqlConnection *conn, QofBook* book) :
GncSqlBackend(conn, book), m_exists{false} {}
+ ~GncDbiBackend();
+ void session_begin(QofSession*, const char*, bool, bool, bool) override;
+ void session_end() override;
+ void load(QofBook*, QofBackendLoadType) override;
+ void safe_sync(QofBook*) override;
bool connected() const noexcept { return m_conn != nullptr; }
/** FIXME: Just a pass-through to m_conn: */
- void set_error(int error, unsigned int repeat, bool retry) noexcept
+ void set_dbi_error(int error, unsigned int repeat, bool retry) noexcept
{
m_conn->set_error(error, repeat, retry);
}
@@ -102,15 +110,14 @@ public:
/*-----*/
bool exists() { return m_exists; }
void set_exists(bool exists) { m_exists = exists; }
- friend void gnc_dbi_load(QofBackend*, QofBook*, QofBackendLoadType);
- friend void gnc_dbi_safe_sync_all(QofBackend*, QofBook*);
private:
+ dbi_conn conn_setup(PairVec& options, UriStrings& uri);
+ bool conn_test_dbi_library(dbi_conn conn);
+ bool set_standard_connection_options(dbi_conn conn, const UriStrings& uri);
+ bool create_database(dbi_conn conn, const char* db);
bool m_exists; // Does the database exist?
};
-
-void gnc_dbi_safe_sync_all (QofBackend* qbe, QofBook* book);
-
/* external access required for tests */
std::string adjust_sql_options_string(const std::string&);
diff --git a/src/backend/dbi/gnc-dbisqlconnection.cpp b/src/backend/dbi/gnc-dbisqlconnection.cpp
index e39dc8f..b3f56d4 100644
--- a/src/backend/dbi/gnc-dbisqlconnection.cpp
+++ b/src/backend/dbi/gnc-dbisqlconnection.cpp
@@ -124,7 +124,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
std::string err{"SQL Backend failed to obtain a transaction: "};
err += errstr;
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
- qof_backend_set_message (m_qbe, err.c_str());
+ m_qbe->set_message (err.c_str());
return false;
}
dbi_result_free(result);
@@ -147,7 +147,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
if (!result)
{
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
- qof_backend_set_message (m_qbe, "Failed to delete lock record");
+ m_qbe->set_message("Failed to delete lock record");
result = dbi_conn_query (m_conn, "ROLLBACK");
if (result)
dbi_result_free (result);
@@ -165,7 +165,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
if (!result)
{
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
- qof_backend_set_message (m_qbe, "Failed to create lock record");
+ m_qbe->set_message("Failed to create lock record");
result = dbi_conn_query (m_conn, "ROLLBACK");
if (result)
dbi_result_free (result);
@@ -179,7 +179,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
qof_backend_set_error(m_qbe, ERR_BACKEND_SERVER_ERR);
std::string err{"Failed to commit transaction: "};
err += errstr;
- qof_backend_set_message(m_qbe, err.c_str());
+ m_qbe->set_message(err.c_str());
return false;
}
dbi_result_free (result);
@@ -189,8 +189,6 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
void
GncDbiSqlConnection::unlock_database ()
{
- GncDbiBackend* qe = reinterpret_cast<decltype(qe)>(m_qbe);
-
if (m_conn == nullptr) return;
g_return_if_fail (dbi_conn_error (m_conn, nullptr) == 0);
@@ -225,7 +223,7 @@ GncDbiSqlConnection::unlock_database ()
if (!result)
{
PERR ("Failed to delete the lock entry");
- qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
+ m_qbe->set_error (ERR_BACKEND_SERVER_ERR);
result = dbi_conn_query (m_conn, "ROLLBACK");
if (result)
{
@@ -262,7 +260,7 @@ GncDbiSqlConnection::unlock_database ()
result = nullptr;
}
PWARN ("Unable to get a lock on LOCK, so failed to clear the lock entry.");
- qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
+ m_qbe->set_error (ERR_BACKEND_SERVER_ERR);
}
GncDbiSqlConnection::~GncDbiSqlConnection()
diff --git a/src/backend/sql/gnc-sql-backend.cpp b/src/backend/sql/gnc-sql-backend.cpp
index 9dd8064..6447746 100644
--- a/src/backend/sql/gnc-sql-backend.cpp
+++ b/src/backend/sql/gnc-sql-backend.cpp
@@ -80,10 +80,8 @@ static EntryVec version_table
};
GncSqlBackend::GncSqlBackend(GncSqlConnection *conn, QofBook* book) :
- qof_be {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr, nullptr, ERR_BACKEND_NO_ERR, nullptr, 0,
- nullptr}, m_conn{conn}, m_book{book}, m_loading{false},
- m_in_query{false}, m_is_pristine_db{false}
+ QofBackend {}, m_conn{conn}, m_book{book}, m_loading{false},
+ m_in_query{false}, m_is_pristine_db{false}
{
if (conn != nullptr)
connect (conn);
@@ -188,15 +186,15 @@ GncSqlBackend::add_columns_to_table(const std::string& table_name,
void
GncSqlBackend::update_progress() const noexcept
{
- if (qof_be.percentage != nullptr)
- (qof_be.percentage) (nullptr, 101.0);
+ if (m_percentage != nullptr)
+ (m_percentage) (nullptr, 101.0);
}
void
GncSqlBackend::finish_progress() const noexcept
{
- if (qof_be.percentage != nullptr)
- (qof_be.percentage) (nullptr, -1.0);
+ if (m_percentage != nullptr)
+ (m_percentage) (nullptr, -1.0);
}
void
@@ -441,7 +439,7 @@ GncSqlBackend::write_schedXactions()
#pragma GCC diagnostic warning "-Wformat-nonliteral"
void
-GncSqlBackend::sync_all(QofBook* book)
+GncSqlBackend::sync(QofBook* book)
{
g_return_if_fail (book != NULL);
@@ -500,8 +498,7 @@ GncSqlBackend::sync_all(QofBook* book)
}
else
{
- if (!qof_backend_check_error (&qof_be))
- qof_backend_set_error (&qof_be, ERR_BACKEND_SERVER_ERR);
+ set_error (ERR_BACKEND_SERVER_ERR);
is_ok = m_conn->rollback_transaction ();
}
finish_progress();
@@ -512,7 +509,7 @@ GncSqlBackend::sync_all(QofBook* book)
/* Routines to deal with the creation of multiple books. */
void
-GncSqlBackend::begin_edit (QofInstance* inst)
+GncSqlBackend::begin(QofInstance* inst)
{
g_return_if_fail (inst != NULL);
@@ -521,7 +518,7 @@ GncSqlBackend::begin_edit (QofInstance* inst)
}
void
-GncSqlBackend::rollback_edit(QofInstance* inst)
+GncSqlBackend::rollback(QofInstance* inst)
{
g_return_if_fail (inst != NULL);
@@ -546,7 +543,7 @@ GncSqlBackend::get_object_backend(const std::string& type) const noexcept
* type and call its commit handler
*/
void
-GncSqlBackend::commit_edit (QofInstance* inst)
+GncSqlBackend::commit (QofInstance* inst)
{
sql_backend be_data;
gboolean is_dirty;
@@ -557,7 +554,7 @@ GncSqlBackend::commit_edit (QofInstance* inst)
if (qof_book_is_readonly(m_book))
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ set_error (ERR_BACKEND_READONLY);
(void)m_conn->rollback_transaction ();
return;
}
diff --git a/src/backend/sql/gnc-sql-backend.hpp b/src/backend/sql/gnc-sql-backend.hpp
index 1ad4608..31a908f 100644
--- a/src/backend/sql/gnc-sql-backend.hpp
+++ b/src/backend/sql/gnc-sql-backend.hpp
@@ -62,11 +62,41 @@ typedef enum
*
* Main SQL backend structure.
*/
-class GncSqlBackend
+class GncSqlBackend : public QofBackend
{
public:
GncSqlBackend(GncSqlConnection *conn, QofBook* book);
virtual ~GncSqlBackend() = default;
+ /**
+ * Load the contents of an SQL database into a book.
+ *
+ * @param book Book to be loaded
+ */
+ void load(QofBook*, QofBackendLoadType) override;
+ /**
+ * Save the contents of a book to an SQL database.
+ *
+ * @param book Book to be saved
+ */
+ void sync(QofBook*) override;
+ /**
+ * An object is about to be edited.
+ *
+ * @param inst Object being edited
+ */
+ void begin(QofInstance*) override;
+ /**
+ * Object editting is complete and the object should be saved.
+ *
+ * @param inst Object being edited
+ */
+ void commit(QofInstance*) override;
+ /**
+ * Object editing has been cancelled.
+ *
+ * @param inst Object being edited
+ */
+ void rollback(QofInstance*) override;
/** Connect the backend to a GncSqlConnection.
* Sets up version info. Calling with nullptr clears the connection and
* destroys the version info.
@@ -158,36 +188,6 @@ public:
uint_t get_table_version(const std::string& table_name) const noexcept;
bool set_table_version (const std::string& table_name, uint_t version) noexcept;
/**
- * Load the contents of an SQL database into a book.
- *
- * @param book Book to be loaded
- */
- void load(QofBook*, QofBackendLoadType);
- /**
- * Save the contents of a book to an SQL database.
- *
- * @param book Book to be saved
- */
- void sync_all(QofBook*);
- /**
- * An object is about to be edited.
- *
- * @param inst Object being edited
- */
- void begin_edit(QofInstance*);
- /**
- * Object editting is complete and the object should be saved.
- *
- * @param inst Object being edited
- */
- void commit_edit(QofInstance*);
- /**
- * Object editing has been cancelled.
- *
- * @param inst Object being edited
- */
- void rollback_edit(QofInstance*);
- /**
* Register a commodity to be committed after loading is complete.
*
* Necessary to save corrections made while loading.
@@ -241,7 +241,6 @@ public:
void finish_progress() const noexcept;
protected:
- QofBackend qof_be; /**< QOF backend. Not a pointer, nor really a member */
GncSqlConnection* m_conn; /**< SQL connection */
QofBook* m_book; /**< The primary, main open book */
bool m_loading; /**< We are performing an initial load */
diff --git a/src/backend/sql/test/utest-gnc-backend-sql.cpp b/src/backend/sql/test/utest-gnc-backend-sql.cpp
index cd7107b..9e8154c 100644
--- a/src/backend/sql/test/utest-gnc-backend-sql.cpp
+++ b/src/backend/sql/test/utest-gnc-backend-sql.cpp
@@ -34,6 +34,17 @@ extern "C"
static const gchar* suitename = "/backend/sql/gnc-backend-sql";
void test_suite_gnc_backend_sql (void);
+
+class GncMockSqlBackend : public GncSqlBackend
+{
+public:
+ GncMockSqlBackend(GncSqlConnection* conn, QofBook* book) :
+ GncSqlBackend(conn, book) {}
+ void session_begin(QofSession*, const char*, bool, bool, bool) override {}
+ void session_end() override {}
+ void safe_sync(QofBook* book) override { sync(book); }
+};
+
class GncMockSqlConnection;
class GncMockSqlResult : public GncSqlResult
@@ -265,7 +276,7 @@ test_gnc_sql_commit_edit (void)
guint dirty_called = 0;
GncMockSqlConnection conn;
const char* msg1 =
- "[GncSqlBackend::commit_edit()] Unknown object type 'null'\n";
+ "[GncSqlBackend::commit()] Unknown object type 'null'\n";
GLogLevelFlags loglevel = static_cast<decltype (loglevel)>
(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
const char* logdomain = "gnc.backend.sql";
@@ -281,7 +292,8 @@ test_gnc_sql_commit_edit (void)
qof_object_initialize ();
auto book = qof_book_new();
- GncSqlBackend sql_be (&conn, book);
+ auto sql_be = new GncMockSqlBackend
+ (&conn, book);
inst = static_cast<decltype (inst)> (g_object_new (QOF_TYPE_INSTANCE, NULL));
qof_instance_init_data (inst, QOF_ID_NULL, book);
qof_book_set_dirty_cb (book, test_dirty_cb, &dirty_called);
@@ -291,7 +303,7 @@ test_gnc_sql_commit_edit (void)
g_assert (qof_instance_get_dirty_flag (inst));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
- sql_be.commit_edit (inst);
+ sql_be->commit(inst);
g_assert (!qof_instance_get_dirty_flag (inst));
g_assert (!qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 0);
@@ -302,7 +314,7 @@ test_gnc_sql_commit_edit (void)
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
- sql_be.commit_edit (QOF_INSTANCE (book));
+ sql_be->commit(QOF_INSTANCE (book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
@@ -313,7 +325,7 @@ test_gnc_sql_commit_edit (void)
g_assert (qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
- sql_be.commit_edit(QOF_INSTANCE (book));
+ sql_be->commit(QOF_INSTANCE (book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (!qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 0);
@@ -322,6 +334,7 @@ test_gnc_sql_commit_edit (void)
g_log_remove_handler (logdomain, hdlr1);
g_object_unref (inst);
g_object_unref (book);
+ delete sql_be;
}
/* handle_and_term
static void
diff --git a/src/backend/xml/gnc-backend-xml.cpp b/src/backend/xml/gnc-backend-xml.cpp
index 7c4088b..d3e2cb2 100644
--- a/src/backend/xml/gnc-backend-xml.cpp
+++ b/src/backend/xml/gnc-backend-xml.cpp
@@ -108,42 +108,11 @@ struct QofXmlBackendProvider : public QofBackendProvider
QofXmlBackendProvider(QofXmlBackendProvider&&) = delete;
QofXmlBackendProvider operator=(QofXmlBackendProvider&&) = delete;
~QofXmlBackendProvider () = default;
- QofBackend* create_backend(void);
+ QofBackend* create_backend(void) { return new GncXmlBackend; }
bool type_check(const char* type);
};
-
-static void
-xml_session_begin (QofBackend* qof_be, QofSession* session,
- const char* book_id, gboolean ignore_lock,
- gboolean create, gboolean force)
-{
- GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
-
- ENTER (" ");
- xml_be->session_begin(session, book_id, ignore_lock, create, force);
- LEAVE (" ");
- return;
-}
-
-/* ================================================================= */
-
-static void
-xml_session_end (QofBackend* qof_be)
-{
- GncXmlBackend* xml_be = (GncXmlBackend*)qof_be;
- ENTER (" ");
- xml_be->session_end();
- LEAVE (" ");
-}
-
-static void
-xml_destroy_backend (QofBackend* qof_be)
-{
- delete reinterpret_cast<GncXmlBackend*>(qof_be);
-}
-
bool
QofXmlBackendProvider::type_check (const char *uri)
{
@@ -201,86 +170,8 @@ det_exit:
return result;
}
-static void
-xml_sync_all (QofBackend* qof_be, QofBook* book)
-{
- GncXmlBackend* xml_be = reinterpret_cast<decltype(xml_be)>(qof_be);
- xml_be->sync(book);
- ENTER ("book=%p, xml_be->m_book=%p", book, xml_be->get_book());
-
- LEAVE ("book=%p", book);
-}
-
-
-static void
-xml_begin_edit (QofBackend* qof_be, QofInstance* inst)
-{
- GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
- xml_be->begin(inst);
-}
-
-static void
-xml_rollback_edit (QofBackend* qof_be, QofInstance* inst)
-{
-
- GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
- xml_be->rollback(inst);
-}
-
-/* ---------------------------------------------------------------------- */
-
-
-/* Load financial data from a file into the book, automatically
- detecting the format of the file, if possible. Return FALSE on
- error, and set the error parameter to indicate what went wrong if
- it's not NULL. This function does not manage file locks in any
- way. */
-
-static void
-gnc_xml_be_load_from_file (QofBackend* qof_be, QofBook* book,
- QofBackendLoadType loadType)
-{
- GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
- xml_be->load(book, loadType);
-}
-
-/* ---------------------------------------------------------------------- */
-
-
-
-static void
-gnc_xml_be_write_accounts_to_file (QofBackend* qof_be, QofBook* book)
-{
- auto datafile = ((GncXmlBackend*)qof_be)->get_filename();
- gnc_book_write_accounts_to_xml_file_v2 (qof_be, book, datafile);
-}
-
/* ================================================================= */
-QofBackend*
-QofXmlBackendProvider::create_backend(void)
-{
-
- auto xml_be = new GncXmlBackend;
- auto qof_be = xml_be->get_qof_be();
- qof_be->session_begin = xml_session_begin;
- qof_be->session_end = xml_session_end;
- qof_be->destroy_backend = xml_destroy_backend;
-
- qof_be->load = gnc_xml_be_load_from_file;
-
- /* The file backend treats accounting periods transactionally. */
- qof_be->begin = xml_begin_edit;
- qof_be->commit = NULL;
- qof_be->rollback = xml_rollback_edit;
-
- qof_be->sync = xml_sync_all;
-
- qof_be->export_fn = gnc_xml_be_write_accounts_to_file;
-
- return qof_be;
-}
-
static void
business_core_xml_init (void)
{
diff --git a/src/backend/xml/gnc-xml-backend.cpp b/src/backend/xml/gnc-xml-backend.cpp
index 213208b..e63b31b 100644
--- a/src/backend/xml/gnc-xml-backend.cpp
+++ b/src/backend/xml/gnc-xml-backend.cpp
@@ -23,6 +23,7 @@ extern "C"
#include <windows.h>
#endif
#include <errno.h>
+#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -48,21 +49,8 @@ extern "C"
#define FILE_URI_PREFIX "file://"
static QofLogModule log_module = GNC_MOD_BACKEND;
-GncXmlBackend::GncXmlBackend()
-{
- memset(&qof_be, 0, sizeof(qof_be));
- qof_backend_init(&qof_be);
-}
-
-GncXmlBackend::~GncXmlBackend()
-{
- /* Stop transaction logging */
- xaccLogSetBaseName (NULL);
- qof_backend_destroy (&qof_be);
-}
-
bool
-check_path (const char* fullpath, QofBackend* qof_be, bool create)
+GncXmlBackend::check_path (const char* fullpath, bool create)
{
struct stat statbuf;
char* dirname = g_path_get_dirname (fullpath);
@@ -78,9 +66,9 @@ check_path (const char* fullpath, QofBackend* qof_be, bool create)
{
/* Error on stat or if it isn't a directory means we
cannot find this filename */
- qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (qof_be, "Couldn't find directory for %s",
- fullpath);
+ set_error(ERR_FILEIO_FILE_NOT_FOUND);
+ std::string msg {"Couldn't find directory for "};
+ set_message(msg + fullpath);
PWARN ("Couldn't find directory for %s", fullpath);
g_free(dirname);
return false;
@@ -91,8 +79,9 @@ check_path (const char* fullpath, QofBackend* qof_be, bool create)
if ((rc != 0) && (!create))
{
/* Error on stat means the file doesn't exist */
- qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (qof_be, "Couldn't find %s", fullpath);
+ set_error(ERR_FILEIO_FILE_NOT_FOUND);
+ std::string msg {"Couldn't find "};
+ set_message(msg + fullpath);
PWARN ("Couldn't find %s", fullpath);
g_free(dirname);
return false;
@@ -105,8 +94,10 @@ check_path (const char* fullpath, QofBackend* qof_be, bool create)
#endif
)
{
- qof_backend_set_error (qof_be, ERR_FILEIO_UNKNOWN_FILE_TYPE);
- qof_backend_set_message (qof_be, "Path %s is a directory", fullpath);
+ set_error(ERR_FILEIO_UNKNOWN_FILE_TYPE);
+ std::string msg {"Path "};
+ msg += fullpath;
+ set_message(msg + " is a directory");
PWARN ("Path %s is a directory", fullpath);
g_free(dirname);
return false;
@@ -123,20 +114,19 @@ GncXmlBackend::session_begin(QofSession* session, const char* book_id,
if (m_fullpath.empty())
{
- qof_backend_set_error (&qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (&qof_be, "No path specified");
+ set_error(ERR_FILEIO_FILE_NOT_FOUND);
+ set_message("No path specified");
return;
}
if (create && !force && save_may_clobber_data())
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_STORE_EXISTS);
+ set_error(ERR_BACKEND_STORE_EXISTS);
PWARN ("Might clobber, no force");
return;
}
- if (!check_path(m_fullpath.c_str(), &qof_be, create))
+ if (!check_path(m_fullpath.c_str(), create))
return;
- qof_be.fullpath = const_cast<char*>(m_fullpath.c_str());
m_dirname = g_path_get_dirname (m_fullpath.c_str());
@@ -159,7 +149,7 @@ GncXmlBackend::session_begin(QofSession* session, const char* book_id,
if (force)
{
- QofBackendError berror = qof_backend_get_error (&qof_be);
+ QofBackendError berror = get_error();
if (berror == ERR_BACKEND_LOCKED || berror == ERR_BACKEND_READONLY)
{
// Even though we couldn't get the lock, we were told to force
@@ -169,7 +159,7 @@ GncXmlBackend::session_begin(QofSession* session, const char* book_id,
else
{
// Unknown error. Push it again on the error stack.
- qof_backend_set_error (&qof_be, berror);
+ set_error(berror);
}
}
}
@@ -181,7 +171,7 @@ GncXmlBackend::session_end()
{
if (m_book && qof_book_is_readonly (m_book))
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ set_error(ERR_BACKEND_READONLY);
return;
}
@@ -307,7 +297,7 @@ GncXmlBackend::load(QofBook* book, QofBackendLoadType loadType)
if (error != ERR_BACKEND_NO_ERR)
{
- qof_backend_set_error (&qof_be, error);
+ set_error(error);
}
/* We just got done loading, it can't possibly be dirty !! */
@@ -329,7 +319,7 @@ GncXmlBackend::sync(QofBook* book)
if (qof_book_is_readonly (m_book))
{
/* Are we read-only? Don't continue in this case. */
- qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ set_error(ERR_BACKEND_READONLY);
return;
}
@@ -348,6 +338,20 @@ GncXmlBackend::save_may_clobber_data()
}
+void
+GncXmlBackend::export_coa(QofBook* book)
+{
+ auto out = fopen(m_fullpath.c_str(), "w");
+ if (out == NULL)
+ {
+ set_error(ERR_FILEIO_WRITE_ERROR);
+ set_message(strerror(errno));
+ return;
+ }
+ gnc_book_write_accounts_to_xml_filehandle_v2(this, book, out);
+ fclose(out);
+}
+
bool
GncXmlBackend::write_to_file (bool make_backup)
{
@@ -358,7 +362,7 @@ GncXmlBackend::write_to_file (bool make_backup)
if (m_book && qof_book_is_readonly (m_book))
{
/* Are we read-only? Don't continue in this case. */
- qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ set_error(ERR_BACKEND_READONLY);
LEAVE ("");
return FALSE;
}
@@ -374,8 +378,8 @@ GncXmlBackend::write_to_file (bool make_backup)
if (!mktemp (tmp_name))
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_MISC);
- qof_backend_set_message (&qof_be, "Failed to make temp file");
+ set_error(ERR_BACKEND_MISC);
+ set_message("Failed to make temp file");
LEAVE ("");
return FALSE;
}
@@ -403,8 +407,8 @@ GncXmlBackend::write_to_file (bool make_backup)
/* Use the permissions from the original data file */
if (g_chmod (tmp_name, statbuf.st_mode) != 0)
{
- /* qof_backend_set_error(&qof_be, ERR_BACKEND_PERM); */
- /* qof_backend_set_message(&qof_be, "Failed to chmod filename %s", tmp_name ); */
+ /* set_error(ERR_BACKEND_PERM); */
+ /* set_message("Failed to chmod filename %s", tmp_name ); */
/* Even if the chmod did fail, the save
nevertheless completed successfully. It is
therefore wrong to signal the ERR_BACKEND_PERM
@@ -423,8 +427,8 @@ GncXmlBackend::write_to_file (bool make_backup)
that. */
if (chown (tmp_name, -1, statbuf.st_gid) != 0)
{
- /* qof_backend_set_error(&qof_be, ERR_BACKEND_PERM); */
- /* qof_backend_set_message(&qof_be, "Failed to chown filename %s", tmp_name ); */
+ /* set_error(ERR_BACKEND_PERM); */
+ /* set_message("Failed to chown filename %s", tmp_name ); */
/* A failed chown doesn't mean that the saving itself
failed. So don't abort with an error here! */
PWARN ("unable to chown filename %s: %s",
@@ -439,7 +443,7 @@ GncXmlBackend::write_to_file (bool make_backup)
}
if (g_unlink (m_fullpath.c_str()) != 0 && errno != ENOENT)
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ set_error(ERR_BACKEND_READONLY);
PWARN ("unable to unlink filename %s: %s",
m_fullpath.empty() ? "(null)" : m_fullpath.c_str(),
g_strerror (errno) ? g_strerror (errno) : "");
@@ -449,16 +453,16 @@ GncXmlBackend::write_to_file (bool make_backup)
}
if (!link_or_make_backup (tmp_name, m_fullpath))
{
- qof_backend_set_error (&qof_be, ERR_FILEIO_BACKUP_ERROR);
- qof_backend_set_message (&qof_be, "Failed to make backup file %s",
- m_fullpath.empty() ? "NULL" : m_fullpath.c_str());
+ set_error(ERR_FILEIO_BACKUP_ERROR);
+ std::string msg{"Failed to make backup file "};
+ set_message(msg + (m_fullpath.empty() ? "NULL" : m_fullpath));
g_free (tmp_name);
LEAVE ("");
return FALSE;
}
if (g_unlink (tmp_name) != 0)
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_PERM);
+ set_error(ERR_BACKEND_PERM);
PWARN ("unable to unlink temp filename %s: %s",
tmp_name ? tmp_name : "(null)",
g_strerror (errno) ? g_strerror (errno) : "");
@@ -492,7 +496,7 @@ GncXmlBackend::write_to_file (bool make_backup)
be_err = ERR_BACKEND_MISC;
break;
}
- qof_backend_set_error (&qof_be, be_err);
+ set_error(be_err);
PWARN ("unable to unlink temp_filename %s: %s",
tmp_name ? tmp_name : "(null)",
g_strerror (errno) ? g_strerror (errno) : "");
@@ -501,9 +505,9 @@ GncXmlBackend::write_to_file (bool make_backup)
else
{
/* Use a generic write error code */
- qof_backend_set_error (&qof_be, ERR_FILEIO_WRITE_ERROR);
- qof_backend_set_message (&qof_be, "Unable to write to temp file %s",
- tmp_name ? tmp_name : "NULL");
+ set_error(ERR_FILEIO_WRITE_ERROR);
+ std::string msg{"Unable to write to temp file "};
+ set_message(msg + (tmp_name ? tmp_name : "NULL"));
}
g_free (tmp_name);
LEAVE ("");
@@ -519,8 +523,8 @@ copy_file (const std::string& orig, const std::string& bkup)
constexpr size_t buf_size = 1024;
char buf[buf_size];
int flags = 0;
- ssize_t count_write;
- ssize_t count_read;
+ ssize_t count_write = 0;
+ ssize_t count_read = 0;
#ifdef G_OS_WIN32
@@ -542,7 +546,7 @@ copy_file (const std::string& orig, const std::string& bkup)
do
{
- auto count_read = read (orig_fd, buf, buf_size);
+ count_read = read (orig_fd, buf, buf_size);
if (count_read == -1 && errno != EINTR)
{
close (orig_fd);
@@ -602,7 +606,7 @@ GncXmlBackend::link_or_make_backup (const std::string& orig,
if (!copy_success)
{
- qof_backend_set_error (&qof_be, ERR_FILEIO_BACKUP_ERROR);
+ set_error(ERR_FILEIO_BACKUP_ERROR);
PWARN ("unable to make file backup from %s to %s: %s",
orig.c_str(), bkup.c_str(), g_strerror (errno) ? g_strerror (errno) : "");
return false;
@@ -626,7 +630,7 @@ GncXmlBackend::get_file_lock ()
if (!rc)
{
/* oops .. file is locked by another user .. */
- qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ set_error(ERR_BACKEND_LOCKED);
return false;
}
@@ -648,7 +652,7 @@ GncXmlBackend::get_file_lock ()
be_err = ERR_BACKEND_LOCKED;
break;
}
- qof_backend_set_error (&qof_be, be_err);
+ set_error(be_err);
return false;
}
@@ -693,7 +697,7 @@ GncXmlBackend::get_file_lock ()
}
/* Otherwise, something else is wrong. */
- qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ set_error(ERR_BACKEND_LOCKED);
g_unlink (linkfile.str().c_str());
close (m_lockfd);
g_unlink (m_lockfile.c_str());
@@ -704,9 +708,9 @@ GncXmlBackend::get_file_lock ()
if (rc)
{
/* oops .. stat failed! This can't happen! */
- qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
- qof_backend_set_message (&qof_be, "Failed to stat lockfile %s",
- m_lockfile.c_str());
+ set_error(ERR_BACKEND_LOCKED);
+ std::string msg{"Failed to stat lockfile "};
+ set_message(msg + m_lockfile);
g_unlink (linkfile.str().c_str());
close (m_lockfd);
g_unlink (m_lockfile.c_str());
@@ -715,7 +719,7 @@ GncXmlBackend::get_file_lock ()
if (statbuf.st_nlink != 2)
{
- qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ set_error(ERR_BACKEND_LOCKED);
g_unlink (linkfile.str().c_str());
close (m_lockfd);
g_unlink (m_lockfile.c_str());
diff --git a/src/backend/xml/gnc-xml-backend.hpp b/src/backend/xml/gnc-xml-backend.hpp
index e49486f..2710115 100644
--- a/src/backend/xml/gnc-xml-backend.hpp
+++ b/src/backend/xml/gnc-xml-backend.hpp
@@ -26,28 +26,26 @@ extern "C"
#include <string>
#include <qof-backend.hpp>
-class GncXmlBackend
+class GncXmlBackend : public QofBackend
{
public:
- GncXmlBackend();
+ GncXmlBackend() = default;
GncXmlBackend(const GncXmlBackend&) = delete;
GncXmlBackend operator=(const GncXmlBackend&) = delete;
GncXmlBackend(const GncXmlBackend&&) = delete;
GncXmlBackend operator=(const GncXmlBackend&&) = delete;
- ~GncXmlBackend();
+ ~GncXmlBackend() = default;
void session_begin(QofSession* session, const char* book_id,
- bool ignore_lock, bool create, bool force);
- void session_end();
- void load(QofBook* book, QofBackendLoadType loadType);
+ bool ignore_lock, bool create, bool force) override;
+ void session_end() override;
+ void load(QofBook* book, QofBackendLoadType loadType) override;
/* The XML backend isn't able to do anything with individual instances. */
- void begin(QofInstance* inst) {}
- void commit(QofInstance* inst) {}
- void rollback(QofInstance* inst) {}
- void sync(QofBook* book);
- QofBackend* get_qof_be() { return &qof_be; }
+ void export_coa(QofBook*) override;
+ void sync(QofBook* book) override;
+ void safe_sync(QofBook* book) override { sync(book); } // XML sync is inherently safe.
const char * get_filename() { return m_fullpath.c_str(); }
QofBook* get_book() { return m_book; }
-
+
private:
bool save_may_clobber_data();
bool get_file_lock();
@@ -56,10 +54,9 @@ private:
bool write_to_file(bool make_backup);
void remove_old_files();
void write_accounts(QofBook* book);
- QofBackend qof_be;
+ bool check_path(const char* fullpath, bool create);
std::string m_dirname;
- std::string m_fullpath; /* Fully qualified path to book */
std::string m_lockfile;
std::string m_linkfile;
int m_lockfd;
diff --git a/src/backend/xml/io-gncxml-v2.cpp b/src/backend/xml/io-gncxml-v2.cpp
index d58e822..c097af8 100644
--- a/src/backend/xml/io-gncxml-v2.cpp
+++ b/src/backend/xml/io-gncxml-v2.cpp
@@ -698,7 +698,6 @@ qof_session_load_from_xml_file_v2_full (
QofBookFileType type)
{
Account* root;
- QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(xml_be);
sixtp_gdv2* gd;
sixtp* top_parser;
sixtp* main_parser;
@@ -707,7 +706,8 @@ qof_session_load_from_xml_file_v2_full (
gboolean retval;
char* v2type = NULL;
- gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, qof_be->percentage);
+ gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
+ xml_be->get_percentage());
top_parser = sixtp_new ();
main_parser = sixtp_new ();
@@ -1340,7 +1340,8 @@ gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
return FALSE;
qof_be = qof_book_get_backend (book);
- gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, qof_be->percentage);
+ gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
+ qof_be->get_percentage());
gd->counter.commodities_total =
gnc_commodity_table_get_size (gnc_commodity_table_get_table (book));
gd->counter.accounts_total = 1 +
@@ -1386,7 +1387,8 @@ gnc_book_write_accounts_to_xml_filehandle_v2 (QofBackend* qof_be, QofBook* book,
|| !write_counts (out, "commodity", ncom, "account", nacc, NULL))
return FALSE;
- gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback, qof_be->percentage);
+ gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback,
+ qof_be->get_percentage());
gd->counter.commodities_total = ncom;
gd->counter.accounts_total = nacc;
@@ -1651,10 +1653,8 @@ gnc_book_write_to_xml_file_v2 (
* postgress or anything else.
*/
gboolean
-gnc_book_write_accounts_to_xml_file_v2 (
- QofBackend* qof_be,
- QofBook* book,
- const char* filename)
+gnc_book_write_accounts_to_xml_file_v2 (QofBackend* qof_be, QofBook* book,
+ const char* filename)
{
FILE* out;
gboolean success = TRUE;
@@ -1671,7 +1671,7 @@ gnc_book_write_accounts_to_xml_file_v2 (
if (out && fclose (out))
success = FALSE;
- if (!success && !qof_backend_check_error (qof_be))
+ if (!success && !qof_be->check_error())
{
/* Use a generic write error code */
diff --git a/src/backend/xml/test/test-dom-converters1.cpp b/src/backend/xml/test/test-dom-converters1.cpp
index c577f9a..5c1d44b 100644
--- a/src/backend/xml/test/test-dom-converters1.cpp
+++ b/src/backend/xml/test/test-dom-converters1.cpp
@@ -32,12 +32,12 @@ extern "C"
#include "test-stuff.h"
#include "test-engine-stuff.h"
-#include "test-file-stuff.h"
#include "cashobjects.h"
#include "gnc-engine.h"
#include "gnc-commodity.h"
}
+#include "test-file-stuff.h"
#include "gnc-xml-helper.h"
#include "sixtp.h"
#include "sixtp-parsers.h"
diff --git a/src/backend/xml/test/test-save-in-lang.cpp b/src/backend/xml/test/test-save-in-lang.cpp
index 04214d0..c9af56e 100644
--- a/src/backend/xml/test/test-save-in-lang.cpp
+++ b/src/backend/xml/test/test-save-in-lang.cpp
@@ -32,11 +32,12 @@ extern "C"
#include "test-stuff.h"
#include "test-engine-stuff.h"
-#include "test-file-stuff.h"
#include "gnc-engine.h"
#include "TransLog.h"
}
+
+#include "test-file-stuff.h"
#include "io-gncxml-v2.h"
const char* possible_envs[] =
diff --git a/src/backend/xml/test/test-string-converters.cpp b/src/backend/xml/test/test-string-converters.cpp
index 8cbf726..66b7572 100644
--- a/src/backend/xml/test/test-string-converters.cpp
+++ b/src/backend/xml/test/test-string-converters.cpp
@@ -26,9 +26,9 @@ extern "C"
#include "test-stuff.h"
#include "test-engine-stuff.h"
-#include "test-file-stuff.h"
}
+#include "test-file-stuff.h"
#include "sixtp-dom-parsers.h"
#include "sixtp-dom-generators.h"
diff --git a/src/backend/xml/test/test-xml-transaction.cpp b/src/backend/xml/test/test-xml-transaction.cpp
index 9247fda..bfe87f7 100644
--- a/src/backend/xml/test/test-xml-transaction.cpp
+++ b/src/backend/xml/test/test-xml-transaction.cpp
@@ -52,7 +52,7 @@ extern "C"
#include "../sixtp-parsers.h"
#include "../sixtp-dom-parsers.h"
#include "../io-gncxml-gen.h"
-#include <test-file-stuff.h>
+#include "test-file-stuff.h"
static QofBook* book;
diff --git a/src/engine/test/utest-Transaction.cpp b/src/engine/test/utest-Transaction.cpp
index 0e06a34..5982ba8 100644
--- a/src/engine/test/utest-Transaction.cpp
+++ b/src/engine/test/utest-Transaction.cpp
@@ -86,47 +86,49 @@ typedef struct
Account *gains_acc;
} GainsFixture;
-typedef struct
-{
- QofBackend be;
- gchar last_call[12];
- QofBackendError result_err;
-} MockBackend;
-
-static void
-mock_backend_set_error (MockBackend *mbe, QofBackendError err)
-{
- mbe->result_err = err;
-}
-
-static void
-mock_backend_rollback (QofBackend *be, QofInstance *foo)
+class MockBackend : public QofBackend
{
- MockBackend *mbe = (MockBackend *)be;
- g_strlcpy (mbe->last_call, "rollback", sizeof (mbe->last_call));
- mbe->be.last_err = mbe->result_err;
-}
-
-static MockBackend*
-mock_backend_new (void)
-{
- MockBackend *mbe = g_new0 (MockBackend, 1);
- mbe->be.rollback = mock_backend_rollback;
- memset (mbe->last_call, 0, sizeof (mbe->last_call));
- return mbe;
-}
+public:
+ MockBackend() : QofBackend(), m_last_call{"Constructor"},
+ m_result_err{ERR_BACKEND_NO_ERR} {}
+ void session_begin(QofSession*, const char*, bool, bool, bool) override {
+ m_last_call = "session_begin";
+ }
+ void session_end() override {
+ m_last_call = "session_end";
+ }
+ void load(QofBook*, QofBackendLoadType) override {
+ m_last_call = "load";
+ }
+ void sync(QofBook*) override {
+ m_last_call = "sync";
+ }
+ void safe_sync(QofBook*) override {
+ m_last_call = "safe_sync";
+ }
+ void rollback(QofInstance*) override {
+ set_error(m_result_err);
+ m_last_call = "rollback";
+ }
+ void inject_error(QofBackendError err) {
+ m_result_err = err;
+ }
+ std::string m_last_call;
+private:
+ QofBackendError m_result_err;
+};
static void
setup (Fixture *fixture, gconstpointer pData)
{
QofBook *book = qof_book_new ();
- MockBackend *mbe = mock_backend_new ();
+ MockBackend *mbe = new MockBackend;
Transaction *txn;
Timespec entered = gnc_dmy2timespec (20, 4, 2012);
Timespec posted = gnc_dmy2timespec (21, 4, 2012);
auto frame = new KvpFrame ();
- qof_book_set_backend (book, (QofBackend*)mbe);
+ qof_book_set_backend (book, mbe);
auto split1 = xaccMallocSplit (book);
auto split2 = xaccMallocSplit (book);
txn = xaccMallocTransaction (book);
@@ -211,14 +213,14 @@ static void
teardown (Fixture *fixture, gconstpointer pData)
{
QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn));
- MockBackend *mbe = (MockBackend *)qof_book_get_backend (book);
+ auto mbe = static_cast<MockBackend*>(qof_book_get_backend (book));
test_destroy (fixture->txn);
test_destroy (fixture->acc1);
test_destroy (fixture->acc2);
test_destroy (fixture->curr);
test_destroy (fixture->comm);
- g_free (mbe);
+ delete mbe;
qof_book_destroy(book);
g_slist_free_full (fixture->hdlrs, test_free_log_handler);
test_clear_error_list();
@@ -1695,7 +1697,7 @@ test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData)
KvpFrame *base_frame = NULL;
auto sig_account = test_signal_new (QOF_INSTANCE (fixture->acc1),
GNC_EVENT_ITEM_CHANGED, NULL);
- MockBackend *mbe = (MockBackend*)qof_book_get_backend (book);
+ auto mbe = static_cast<MockBackend*>(qof_book_get_backend (book));
auto split_00 = static_cast<Split*>(txn->splits->data);
auto split_01 = static_cast<Split*>(txn->splits->next->data);
auto split_02 = xaccMallocSplit (book);
@@ -1743,7 +1745,7 @@ test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData)
FALSE, FALSE, FALSE));
g_assert (xaccSplitEqual (static_cast<Split*>(txn->splits->next->data),
split_10, FALSE, FALSE, FALSE));
- g_assert_cmpstr (mbe->last_call, ==, "rollback");
+ g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback");
g_assert_cmpuint (qof_instance_get_editlevel (QOF_INSTANCE (txn)), ==, 0);
g_assert (qof_instance_get_destroying (txn) == FALSE);
test_signal_free (sig_account);
@@ -1757,7 +1759,7 @@ test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData)
static void
test_xaccTransRollbackEdit_BackendErrors (Fixture *fixture, gconstpointer pData)
{
- MockBackend *mbe = (MockBackend*)qof_book_get_backend (qof_instance_get_book (fixture->txn));
+ auto mbe = static_cast<MockBackend*>(qof_book_get_backend (qof_instance_get_book (fixture->txn)));
auto loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
auto msg = "[xaccTransRollbackEdit()] Rollback Failed. Ouch!";
auto check = test_error_struct_new ("gnc.engine", loglevel, msg);
@@ -1765,16 +1767,16 @@ test_xaccTransRollbackEdit_BackendErrors (Fixture *fixture, gconstpointer pData)
(GLogFunc)test_checked_handler);
g_object_ref (fixture->txn);
xaccTransBeginEdit (fixture->txn);
- mock_backend_set_error (mbe, ERR_BACKEND_MODIFIED);
+ mbe->inject_error(ERR_BACKEND_MODIFIED);
xaccTransRollbackEdit (fixture->txn);
g_assert_cmpint (check->hits, ==, 1);
- g_assert_cmpstr (mbe->last_call, ==, "rollback");
- memset (mbe->last_call, 0, sizeof (mbe->last_call));
+ g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback");
+ mbe->m_last_call.clear();
xaccTransBeginEdit (fixture->txn);
- mock_backend_set_error (mbe, ERR_BACKEND_MOD_DESTROY);
+ mbe->inject_error (ERR_BACKEND_MOD_DESTROY);
xaccTransRollbackEdit (fixture->txn);
g_assert_cmpint (GPOINTER_TO_INT(fixture->txn->num), ==, 1);
- g_assert_cmpstr (mbe->last_call, ==, "rollback");
+ g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback");
}
/* xaccTransIsOpen C: 23 in 7 SCM: 1 Local: 0:0:0
@@ -1941,7 +1943,7 @@ test_xaccTransReverse (Fixture *fixture, gconstpointer pData)
g_assert (guid_equal (frame->get_slot(TRANS_REVERSED_BY)->get<GncGUID*>(),
xaccTransGetGUID (rev)));
- g_assert (qof_instance_is_dirty (QOF_INSTANCE (rev)));
+ g_assert (!qof_instance_is_dirty (QOF_INSTANCE (rev))); //Cleared by commit
g_assert_cmpint (g_list_length (fixture->txn->splits), ==,
g_list_length (rev->splits));
for (orig_splits = fixture->txn->splits,
diff --git a/src/libqof/qof/qof-backend.cpp b/src/libqof/qof/qof-backend.cpp
index 28feadc..e779fc0 100644
--- a/src/libqof/qof/qof-backend.cpp
+++ b/src/libqof/qof/qof-backend.cpp
@@ -25,17 +25,14 @@
extern "C"
{
-#include "config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <regex.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <errno.h>
+#include <config.h>
#include "qof.h"
}
+#include <string>
+#include <algorithm>
+#include <vector>
+
#include "qof-backend.hpp"
G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_BACKEND;
@@ -47,238 +44,128 @@ G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_BACKEND;
* error handling *
\********************************************************************/
+GModuleVec QofBackend::c_be_registry{};
+
void
-qof_backend_set_error (QofBackend *be, QofBackendError err)
+QofBackend::set_error(QofBackendError err)
{
- if (!be) return;
-
/* use stack-push semantics. Only the earliest error counts */
- if (ERR_BACKEND_NO_ERR != be->last_err) return;
- be->last_err = err;
+ if (m_last_err != ERR_BACKEND_NO_ERR) return;
+ m_last_err = err;
}
QofBackendError
-qof_backend_get_error (QofBackend *be)
+QofBackend::get_error()
{
- QofBackendError err;
- if (!be) return ERR_BACKEND_NO_BACKEND;
-
/* use 'stack-pop' semantics */
- err = be->last_err;
- be->last_err = ERR_BACKEND_NO_ERR;
+ auto err = m_last_err;
+ m_last_err = ERR_BACKEND_NO_ERR;
return err;
}
-gboolean
-qof_backend_check_error (QofBackend *be)
+bool
+QofBackend::check_error()
{
- g_return_val_if_fail (be != NULL, TRUE);
- return be->last_err != ERR_BACKEND_NO_ERR;
+ return m_last_err != ERR_BACKEND_NO_ERR;
}
-gboolean
-qof_backend_can_rollback (QofBackend* be)
+void
+QofBackend::set_message (std::string&& msg)
{
- if (be == nullptr) return FALSE;
- return be->rollback != nullptr;
+ m_error_msg = msg;
}
-void
-qof_backend_rollback_instance (QofBackend* be, QofInstance* inst)
+const std::string&&
+QofBackend::get_message ()
{
- if (be == nullptr || be->rollback == nullptr) return;
- (be->rollback)(be, inst);
+ return std::move(m_error_msg);
}
-void
-qof_backend_set_message (QofBackend *be, const char *format, ...)
+bool
+QofBackend::register_backend(const char* directory, const char* module_name)
{
- va_list args;
- char * buffer;
-
- if (!be) return;
-
- /* If there's already something here, free it */
- if (be->error_msg) g_free(be->error_msg);
-
- if (!format)
+ if (!g_module_supported ())
{
- be->error_msg = NULL;
- return;
+ PWARN("Modules not supported.");
+ return false;
}
+ auto fullpath = g_module_build_path (directory, module_name);
+/* Darwin modules can have either .so or .dylib for a suffix */
+ if (!g_file_test (fullpath, G_FILE_TEST_EXISTS) &&
+ g_strcmp0 (G_MODULE_SUFFIX, "so") == 0)
+ {
+ auto modname = g_strdup_printf ("lib%s.dylib", module_name);
+ g_free (fullpath);
+ fullpath = g_build_filename (directory, modname, NULL);
+ g_free (modname);
+ }
+ auto backend = g_module_open (fullpath, G_MODULE_BIND_LAZY);
+ g_free (fullpath);
+ if (!backend)
+ {
+ PINFO ("%s: %s\n", PACKAGE, g_module_error ());
+ return false;
+ }
+ void (*module_init_func)(void);
+ if (g_module_symbol (backend, "qof_backend_module_init",
+ reinterpret_cast<void**>(&module_init_func)))
+ module_init_func ();
- va_start(args, format);
- buffer = (char *)g_strdup_vprintf(format, args);
- va_end(args);
-
- be->error_msg = buffer;
-}
-
-char *
-qof_backend_get_message (QofBackend *be)
-{
- char * msg;
-
- if (!be) return g_strdup("ERR_BACKEND_NO_BACKEND");
- if (!be->error_msg) return NULL;
-
- /*
- * Just return the contents of the error_msg and then set it to
- * NULL. This is necessary, because the Backends don't seem to
- * have a destroy_backend function to take care of freeing stuff
- * up. The calling function should free the copy.
- * Also, this is consistent with the qof_backend_get_error() popping.
- */
-
- msg = be->error_msg;
- be->error_msg = NULL;
- return msg;
+ g_module_make_resident (backend);
+ c_be_registry.push_back(backend);
+ return TRUE;
}
-/***********************************************************************/
-/* Get a clean backend */
void
-qof_backend_init(QofBackend *be)
+QofBackend::release_backends()
{
- be->session_begin = NULL;
- be->session_end = NULL;
- be->destroy_backend = NULL;
-
- be->load = NULL;
-
- be->begin = NULL;
- be->commit = NULL;
- be->rollback = NULL;
-
- be->sync = NULL;
- be->safe_sync = NULL;
-
- be->export_fn = NULL;
-
- be->last_err = ERR_BACKEND_NO_ERR;
- if (be->error_msg) g_free (be->error_msg);
- be->error_msg = NULL;
- be->percentage = NULL;
+ for (auto backend : c_be_registry)
+ {
+ void (*module_finalize_func)(void);
+ if (g_module_symbol(backend, "qof_backend_module_finalize",
+ reinterpret_cast<void**>(&module_finalize_func)))
+ module_finalize_func();
+ }
}
-
-void
-qof_backend_destroy(QofBackend *be)
+/***********************************************************************/
+QofBackendError
+qof_backend_get_error (QofBackend* qof_be)
{
- g_free(be->error_msg);
- be->error_msg = NULL;
+ if (qof_be == nullptr) return ERR_BACKEND_NO_ERR;
+ return ((QofBackend*)qof_be)->get_error();
}
void
-qof_backend_run_begin(QofBackend *be, QofInstance *inst)
+qof_backend_set_error (QofBackend* qof_be, QofBackendError err)
{
- if (!be || !inst)
- {
- return;
- }
- if (!be->begin)
- {
- return;
- }
- (be->begin) (be, inst);
+ if (qof_be == nullptr) return;
+ ((QofBackend*)qof_be)->set_error(err);
}
gboolean
-qof_backend_begin_exists(const QofBackend *be)
+qof_backend_can_rollback (QofBackend* qof_be)
{
- if (be->begin)
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
+ if (qof_be == nullptr) return FALSE;
+ return true;
}
void
-qof_backend_run_commit(QofBackend *be, QofInstance *inst)
+qof_backend_rollback_instance (QofBackend* qof_be, QofInstance* inst)
{
- if (!be || !inst)
- {
- return;
- }
- if (!be->commit)
- {
- return;
- }
- (be->commit) (be, inst);
+ if (qof_be == nullptr) return;
+ ((QofBackend*)qof_be)->rollback(inst);
}
-
-gboolean
-qof_backend_commit_exists(const QofBackend *be)
-{
- if (!be)
- {
- return FALSE;
- }
- if (be->commit)
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
-}
-
-static GSList* backend_module_list = NULL;
-
gboolean
qof_load_backend_library (const char *directory, const char* module_name)
{
- gchar *fullpath;
- GModule *backend;
- void (*module_init_func) (void);
-
- g_return_val_if_fail(g_module_supported (), FALSE);
- fullpath = g_module_build_path (directory, module_name);
-/* Darwin modules can have either .so or .dylib for a suffix */
- if (!g_file_test (fullpath, G_FILE_TEST_EXISTS) &&
- g_strcmp0 (G_MODULE_SUFFIX, "so") == 0)
- {
- gchar *modname = g_strdup_printf ("lib%s.dylib", module_name);
- g_free (fullpath);
- fullpath = g_build_filename (directory, modname, NULL);
- g_free (modname);
- }
- backend = g_module_open (fullpath, G_MODULE_BIND_LAZY);
- g_free (fullpath);
- if (!backend)
- {
- g_message ("%s: %s\n", PACKAGE, g_module_error ());
- return FALSE;
- }
- if (g_module_symbol (backend, "qof_backend_module_init",
- reinterpret_cast<void**>(&module_init_func)))
- module_init_func ();
-
- g_module_make_resident (backend);
- backend_module_list = g_slist_prepend (backend_module_list, backend);
- return TRUE;
+ return QofBackend::register_backend(directory, module_name);
}
void
qof_finalize_backend_libraries(void)
{
- GSList* node;
- GModule* backend;
- void (*module_finalize_func) (void);
-
- for (node = backend_module_list; node != NULL; node = node->next)
- {
- backend = (GModule*)node->data;
-
- if (g_module_symbol(backend, "qof_backend_module_finalize",
- reinterpret_cast<void**>(&module_finalize_func)))
- module_finalize_func();
-
- }
+ QofBackend::release_backends();
}
/************************* END OF FILE ********************************/
diff --git a/src/libqof/qof/qof-backend.hpp b/src/libqof/qof/qof-backend.hpp
index 9eb0e65..aebec9a 100644
--- a/src/libqof/qof/qof-backend.hpp
+++ b/src/libqof/qof/qof-backend.hpp
@@ -1,5 +1,6 @@
/********************************************************************\
- * qofbackend-p.h -- private api for data storage backend *
+ * qof-backend.hpp Declare QofBackend class *
+ * Copyright 2016 John Ralls <jralls at ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -38,81 +39,26 @@
@author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
@{ */
-#ifndef QOF_BACKEND_P_H
-#define QOF_BACKEND_P_H
-
+#ifndef __QOF_BACKEND_HPP__
+#define __QOF_BACKEND_HPP__
+extern "C"
+{
#include "qofbackend.h"
#include "qofbook.h"
#include "qofinstance-p.h"
#include "qofquery.h"
#include "qofsession.h"
+#include <gmodule.h>
+}
-/**
- * The backend_new routine sets the functions that will be used
- * by the backend to perform the actions required by QOF. A
- * basic minimum is session_begin, session_end, load and
- * sync. Any unused functions should be set to NULL. If the
- * backend uses configuration options, backend_new must ensure
- * that these are set to usable defaults before returning. To use
- * configuration options, load_config and get_config must also
- * be defined.
- *
- * The session_begin() routine gives the backend a second initialization
- * opportunity. It is suggested that the backend check that
- * the URL is syntactically correct, and that it is actually
- * reachable. This is probably(?) a good time to initialize
- * the actual network connection.
- *
- * The 'ignore_lock' argument indicates whether the single-user
- * lock on the backend should be cleared. The typical GUI sequence
- * leading to this is: (1) GUI attempts to open the backend
- * by calling this routine with FALSE==ignore_lock. (2) If backend
- * error'ed BACKEND_LOCK, then GUI asks user what to do. (3) if user
- * answers 'break & enter' then this routine is called again with
- * TRUE==ignore_lock.
- *
- * The 'create_if_nonexistent' argument indicates whether this
- * routine should create a new 'database', if it doesn't already
- * exist. For example, for a file-backend, this would create the
- * file, if it didn't already exist. For an SQL backend, this
- * would create the database (the schema) if it didn't already
- * exist. This flag is used to implement the 'SaveAs' GUI, where
- * the user requests to save data to a new backend.
- *
- * The load() routine should load the minimal set of application data
- * needed for the application to be operable at initial startup.
- * It is assumed that the application will perform a 'run_query()'
- * to obtain any additional data that it needs. For file-based
- * backends, it is acceptable for the backend to return all data
- * at load time; for SQL-based backends, it is acceptable for the
- * backend to return no data.
- *
- * Thus, for example, the GnuCash postgres backend returned
- * the account tree, all currencies, and the pricedb, as these
- * were needed at startup. It did not have to return any
- * transactions whatsoever, as these were obtained at a later stage
- * when a user opened a register, resulting in a query being sent to
- * the backend.
- *
- * (Its OK to send over entities at this point, but one should
- * be careful of the network load; also, its possible that whatever
- * is sent is not what the user wanted anyway, which is why its
- * better to wait for the query).
- *
- * The begin() routine is called when the engine is about to
- * make a change to a data structure. It can provide an advisory
- * lock on data.
- *
- * The commit() routine commits the changes from the engine to the
- * backend data storage.
- *
- * The rollback() routine is used to revert changes in the engine
- * and unlock the backend.
- *
- * If the second user tries to modify an entity that
- * the first user deleted, then the backend should set the error
- * to ERR_BACKEND_MOD_DESTROY from this routine, so that the
- * engine can properly clean up.
+#include <string>
+#include <algorithm>
+#include <vector>
+/* NOTE: The following comments were musings by the original developer about how
+ * some additional API might work. The compile/free/run_query functions were
+ * implemented for the DBI backend but never put into use; the rest were never
+ * implemented. They're here as something to consider if we ever decide to
+ * implement them.
*
* The compile_query() method compiles a QOF query object into
* a backend-specific data structure and returns the compiled
@@ -140,22 +86,6 @@
* continue functioning even when disconnected from the server:
* this is because it will have its local cache of data from which to work.
*
- * The sync() routine synchronizes the engine contents to the backend.
- * This should done by using version numbers (hack alert -- the engine
- * does not currently contain version numbers).
- * If the engine contents are newer than what is in the backend, the
- * data is stored to the backend. If the engine contents are older,
- * then the engine contents are updated.
- *
- * Note that this sync operation is only meant to apply to the
- * current contents of the engine. This routine is not intended
- * to be used to fetch entity data from the backend.
- *
- * File based backends tend to use sync as if it was called dump.
- * Data is written out into the backend, overwriting the previous
- * data. Database backends should implement a more intelligent
- * solution.
- *
* The events_pending() routines should return true if there are
* external events which need to be processed to bring the
* engine up to date with the backend.
@@ -164,10 +94,6 @@
* by the events_pending() routine. It should return TRUE if
* the engine was changed while engine events were suspended.
*
- * The last_err member indicates the last error that occurred.
- * It should probably be implemented as an array (actually,
- * a stack) of all the errors that have occurred.
- *
* For support of book partitioning, use special "Book" begin_edit()
* and commit_edit() QOF_ID types.
*
@@ -242,80 +168,147 @@ typedef enum
LOAD_TYPE_LOAD_ALL
} QofBackendLoadType;
-struct QofBackend_s
+using GModuleVec = std::vector<GModule*>;
+struct QofBackend
{
- void (*session_begin) (QofBackend *be,
- QofSession *session,
- const char *book_id,
- gboolean ignore_lock,
- gboolean create,
- gboolean force);
- void (*session_end) (QofBackend *);
- void (*destroy_backend) (/*@ only @*/ QofBackend *);
-
- void (*load) (QofBackend *, /*@ dependent @*/ QofBook *, QofBackendLoadType);
-
- void (*begin) (QofBackend *, QofInstance *);
- void (*commit) (QofBackend *, QofInstance *);
- void (*rollback) (QofBackend *, QofInstance *);
-
- void (*sync) (QofBackend *, /*@ dependent @*/ QofBook *);
- void (*safe_sync) (QofBackend *, /*@ dependent @*/ QofBook *);
- /* This is implented only in the XML backend where it exports only a chart
- * of accounts.
+public:
+ /* For reasons that aren't a bit clear, using the default constructor
+ * sometimes initializes m_last_err incorrectly with Xcode8 and a 32-bit
+ * build unless the initialization is stepped-through in a debugger.
*/
- void (*export_fn) (QofBackend *, QofBook *);
- QofBePercentageFunc percentage;
-
- QofBackendError last_err;
- char * error_msg;
-
- gint config_count;
+ QofBackend() :
+ m_percentage{nullptr}, m_fullpath{}, m_last_err{ERR_BACKEND_NO_ERR},
+ m_error_msg{} {}
+ QofBackend(const QofBackend&) = delete;
+ QofBackend(const QofBackend&&) = delete;
+ virtual ~QofBackend() = default;
+/**
+ * Open the file or connect to the server.
+ * @param session The QofSession that will control the backend.
+ * @param book_id The book's string identifier.
+ * @param ignore_lock indicates whether the single-user lock on the backend
+ * should be cleared. The typical GUI sequence leading to this is:
+ * (1) GUI attempts to open the backend by calling this routine with
+ * ignore_lock false.
+ * (2) If backend error'ed BACKEND_LOCK, then GUI asks user what to do.
+ * (3) if user answers 'break & enter' then this routine is called again with
+ * ignore_lock true.
+ * @param create indicates whether this routine should create a new
+ * 'database', if it doesn't already exist. For example, for a file-backend,
+ * this would create the file, if it didn't already exist. For an SQL
+ * backend, this would create the database (the schema) if it didn't already
+ * exist. This flag is used to implement the 'SaveAs' GUI, where the user
+ * requests to save data to a new backend.
+ *
+ * @param force works with create to force creating a new database even if
+ * one already exists at the same URI.
+ */
+ virtual void session_begin(QofSession *session, const char* book_id,
+ bool ignore_lock, bool create, bool force) = 0;
+ virtual void session_end() = 0;
+/**
+ * Load the minimal set of application data needed for the application to be
+ * operable at initial startup. It is assumed that the application will
+ * perform a 'run_query()' to obtain any additional data that it needs. For
+ * file-based backends, it is acceptable for the backend to return all data
+ * at load time; for SQL-based backends, it is acceptable for the backend to
+ * return no data.
+ *
+ * Thus, for example, the old GnuCash postgres backend returned the account
+ * tree, all currencies, and the pricedb, as these were needed at startup.
+ * It did not have to return any transactions whatsoever, as these were
+ * obtained at a later stage when a user opened a register, resulting in a
+ * query being sent to the backend. The current DBI backend on the other hand
+ * loads the entire database into memory.
+ *
+ * (Its OK to send over entities at this point, but one should
+ * be careful of the network load; also, its possible that whatever
+ * is sent is not what the user wanted anyway, which is why its
+ * better to wait for the query).
+ */
+ virtual void load (QofBook*, QofBackendLoadType) = 0;
+/**
+ * Called when the engine is about to make a change to a data structure. It
+ * could provide an advisory lock on data, but no backend does this.
+ */
+ virtual void begin(QofInstance*) {}
+/**
+ * Commits the changes from the engine to the backend data storage.
+ */
+ virtual void commit (QofInstance*) {}
+/**
+ * Revert changes in the engine and unlock the backend.
+ */
+ virtual void rollback(QofInstance*) {}
+/**
+ * Synchronizes the engine contents to the backend.
+ * This should done by using version numbers (hack alert -- the engine
+ * does not currently contain version numbers).
+ * If the engine contents are newer than what is in the backend, the
+ * data is stored to the backend. If the engine contents are older,
+ * then the engine contents are updated.
+ *
+ * Note that this sync operation is only meant to apply to the
+ * current contents of the engine. This routine is not intended
+ * to be used to fetch entity data from the backend.
+ *
+ * File based backends tend to use sync as if it was called dump.
+ * Data is written out into the backend, overwriting the previous
+ * data. Database backends should implement a more intelligent
+ * solution.
+ */
+ virtual void sync(QofBook *) = 0;
+/** Perform a sync in a way that prevents data loss on a DBI backend.
+ */
+ virtual void safe_sync(QofBook *) = 0;
+/** Extract the chart of accounts from the current database and create a new
+ * database with it. Implemented only in the XML backend at present.
+ */
+ virtual void export_coa(QofBook *) {}
+/** Set the error value only if there isn't already an error already.
+ */
+ void set_error(QofBackendError err);
+/** Retrieve the currently-stored error and clear it.
+ */
+ QofBackendError get_error();
+/** Report if there is an error.
+ */
+ bool check_error();
+/** Set a descriptive message that can be displayed to the user when there's an
+ * error.
+ */
+ void set_message(std::string&&);
+/** Retrieve and clear the stored error message.
+ */
+ const std::string&& get_message();
+/** Store and retrieve a backend-specific function for determining the progress
+ * in completing a long operation, for use with a progress meter.
+ */
+ void set_percentage(QofBePercentageFunc pctfn) { m_percentage = pctfn; }
+ QofBePercentageFunc get_percentage() { return m_percentage; }
+/** Retrieve the backend's storage URI.
+ */
+ std::string get_uri() { return m_fullpath; }
+/**
+ * Class methods for dynamically loading the several backends and for freeing
+ * them at shutdown.
+ */
+ static bool register_backend(const char*, const char*);
+ static void release_backends();
+protected:
+ QofBePercentageFunc m_percentage;
/** Each backend resolves a fully-qualified file path.
* This holds the filepath and communicates it to the frontends.
*/
- char * fullpath;
+ std::string m_fullpath;
+private:
+ static GModuleVec c_be_registry;
+ QofBackendError m_last_err;
+ std::string m_error_msg;
};
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/** The qof_backend_set_message() assigns a string to the backend error message.
- */
-void qof_backend_set_message(QofBackend *be, const char *format, ...);
-
-/** The qof_backend_get_message() pops the error message string from
- * the Backend. This string should be freed with g_free().
- */
-char * qof_backend_get_message(QofBackend *be);
-
-void qof_backend_init(QofBackend *be);
-void qof_backend_destroy(QofBackend *be);
-
-/** Allow backends to see if the book is open
-
- at return 'y' if book is open, otherwise 'n'.
-*/
-gchar qof_book_get_open_marker(const QofBook *book);
-
-/** get the book version
-
-used for tracking multiuser updates in backends.
-
- at return -1 if no book exists, 0 if the book is
-new, otherwise the book version number.
-*/
-gint32 qof_book_get_version (const QofBook *book);
-
-void qof_book_set_version (QofBook *book, gint32 version);
-
/* @} */
/* @} */
/* @} */
-#ifdef __cplusplus
-}
-#endif
-#endif /* QOF_BACKEND_P_H */
+#endif /* __QOF_BACKEND_HPP__ */
diff --git a/src/libqof/qof/qofbackend.h b/src/libqof/qof/qofbackend.h
index 1fa87c9..98e9a9a 100644
--- a/src/libqof/qof/qofbackend.h
+++ b/src/libqof/qof/qofbackend.h
@@ -127,15 +127,8 @@ typedef enum
ERR_RPC_FAILED, /**< Operation failed */
ERR_RPC_NOT_ADDED, /**< object not added */
} QofBackendError;
-/** \brief Pseudo-object providing an interface between the
- * engine and a persistant data store (e.g. a server, a database,
- * or a file).
- *
- * There are no backend functions that are 'public' to users of the
- * engine. The backend can, however, report errors to the GUI & other
- * front-end users.
- */
- typedef struct QofBackend_s QofBackend;
+
+typedef struct QofBackend QofBackend;
/* The following functions are used in C files. */
/** Get the last backend error. */
@@ -143,7 +136,7 @@ typedef enum
/** Set the error on the specified QofBackend. */
void qof_backend_set_error (QofBackend*, QofBackendError);
-/* Temporary wrapper so that we don't have to expose qofbackend-p.h to Transaction.c */
+/* Temporary wrapper so that we don't have to expose qof-backend.hpp to Transaction.c */
gboolean qof_backend_can_rollback (QofBackend*);
void qof_backend_rollback_instance (QofBackend*, QofInstance*);
@@ -173,30 +166,7 @@ typedef void (*QofBePercentageFunc) (/*@ null @*/ const char *message, double pe
#ifdef __cplusplus
}
-
-/** @name Allow access to the begin routine for this backend. */
-//@{
-
-void qof_backend_run_begin(QofBackend *be, QofInstance *inst);
-
-gboolean qof_backend_begin_exists(const QofBackend *be);
-
-void qof_backend_run_commit(QofBackend *be, QofInstance *inst);
-
-gboolean qof_backend_commit_exists(const QofBackend *be);
-//@}
-
-/** Report if the backend is in an error state.
- * Since get_error resets the error state, its use for branching as the backend
- * bubbles back up to the session would make the session think that there was
- * no error.
- * \param be The backend being tested.
- * \return TRUE if the backend has an error set.
- */
-gboolean qof_backend_check_error (QofBackend *be);
-
#endif
-
#endif /* QOF_BACKEND_H */
/** @} */
/** @} */
diff --git a/src/libqof/qof/qofinstance.cpp b/src/libqof/qof/qofinstance.cpp
index 5e923ea..64feeef 100644
--- a/src/libqof/qof/qofinstance.cpp
+++ b/src/libqof/qof/qofinstance.cpp
@@ -958,7 +958,6 @@ gboolean
qof_begin_edit (QofInstance *inst)
{
QofInstancePrivate *priv;
- QofBackend * be;
if (!inst) return FALSE;
@@ -968,9 +967,9 @@ qof_begin_edit (QofInstance *inst)
if (0 >= priv->editlevel)
priv->editlevel = 1;
- be = qof_book_get_backend(priv->book);
- if (be && qof_backend_begin_exists(be))
- qof_backend_run_begin(be, inst);
+ auto be = qof_book_get_backend(priv->book);
+ if (be)
+ be->begin(inst);
else
priv->dirty = TRUE;
@@ -1002,7 +1001,6 @@ qof_commit_edit_part2(QofInstance *inst,
void (*on_free)(QofInstance *))
{
QofInstancePrivate *priv;
- QofBackend * be;
priv = GET_PRIVATE(inst);
@@ -1013,27 +1011,27 @@ qof_commit_edit_part2(QofInstance *inst,
}
/* See if there's a backend. If there is, invoke it. */
- be = qof_book_get_backend(priv->book);
- if (be && qof_backend_commit_exists(be))
+ auto be = qof_book_get_backend(priv->book);
+ if (be)
{
QofBackendError errcode;
/* clear errors */
do
{
- errcode = qof_backend_get_error(be);
+ errcode = be->get_error();
}
- while (ERR_BACKEND_NO_ERR != errcode);
+ while (errcode != ERR_BACKEND_NO_ERR);
- qof_backend_run_commit(be, inst);
- errcode = qof_backend_get_error(be);
- if (ERR_BACKEND_NO_ERR != errcode)
+ be->commit(inst);
+ errcode = be->get_error();
+ if (errcode != ERR_BACKEND_NO_ERR)
{
/* XXX Should perform a rollback here */
priv->do_free = FALSE;
/* Push error back onto the stack */
- qof_backend_set_error (be, errcode);
+ be->set_error (errcode);
if (on_error)
on_error(inst, errcode);
return FALSE;
diff --git a/src/libqof/qof/qofsession.cpp b/src/libqof/qof/qofsession.cpp
index 91e04c6..9939c67 100644
--- a/src/libqof/qof/qofsession.cpp
+++ b/src/libqof/qof/qofsession.cpp
@@ -122,6 +122,7 @@ QofSessionImpl::QofSessionImpl () noexcept
: m_book {qof_book_new ()},
m_book_id {},
m_saving {false},
+ m_last_err {},
m_error_message {}
{
clear_error ();
@@ -157,10 +158,7 @@ QofSessionImpl::destroy_backend () noexcept
if (backend)
{
clear_error ();
- if (backend->destroy_backend)
- backend->destroy_backend (backend);
- else
- g_free(backend);
+ delete backend;
qof_book_set_backend (m_book, nullptr);
}
}
@@ -218,7 +216,7 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
* top-level account group out of the backend, and that is a
* generic, backend-independent operation.
*/
- QofBackend * be {qof_book_get_backend (oldbook)};
+ auto be (qof_book_get_backend (oldbook));
qof_book_set_backend (newbook, be);
/* Starting the session should result in a bunch of accounts
@@ -227,12 +225,9 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
*/
if (be)
{
- be->percentage = percentage_func;
- if (be->load)
- {
- be->load (be, newbook, LOAD_TYPE_INITIAL_LOAD);
- push_error (qof_backend_get_error(be), {});
- }
+ be->set_percentage(percentage_func);
+ be->load (newbook, LOAD_TYPE_INITIAL_LOAD);
+ push_error (be->get_error(), {});
}
/* XXX if the load fails, then we try to restore the old set of books;
@@ -262,7 +257,8 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
}
void
-QofSessionImpl::begin (std::string new_book_id, bool ignore_lock, bool create, bool force) noexcept
+QofSessionImpl::begin (std::string new_book_id, bool ignore_lock,
+ bool create, bool force) noexcept
{
ENTER (" sess=%p ignore_lock=%d, book-id=%s",
this, ignore_lock, new_book_id.c_str ());
@@ -313,7 +309,8 @@ QofSessionImpl::begin (std::string new_book_id, bool ignore_lock, bool create, b
g_free (scheme);
/* No backend was found. That's bad. */
- if (!qof_book_get_backend (m_book))
+ auto backend = qof_book_get_backend (m_book);
+ if (backend == nullptr)
{
m_book_id = {};
if (ERR_BACKEND_NO_ERR == get_error ())
@@ -324,26 +321,22 @@ QofSessionImpl::begin (std::string new_book_id, bool ignore_lock, bool create, b
}
/* If there's a begin method, call that. */
- if (qof_book_get_backend (m_book)->session_begin)
+ backend->session_begin(this, m_book_id.c_str(), ignore_lock, create, force);
+ PINFO ("Done running session_begin on backend");
+ QofBackendError const err {backend->get_error()};
+ auto msg (backend->get_message());
+ if (err != ERR_BACKEND_NO_ERR)
{
- auto backend = qof_book_get_backend (m_book);
- (backend->session_begin) (backend, this, m_book_id.c_str (), ignore_lock, create, force);
- PINFO ("Done running session_begin on backend");
- QofBackendError const err {qof_backend_get_error (backend)};
- char * msg {qof_backend_get_message (backend)};
- if (err != ERR_BACKEND_NO_ERR)
- {
- m_book_id = {};
- push_error (err, msg ? msg : "");
- LEAVE (" backend error %d %s", err, msg ? msg : "(null)");
- return;
- }
- if (msg != nullptr)
- {
- PWARN("%s", msg);
- g_free(msg);
- }
+ m_book_id = {};
+ push_error (err, msg);
+ LEAVE (" backend error %d %s", err, msg.empty() ? "(null)" : msg.c_str());
+ return;
+ }
+ if (!msg.empty())
+ {
+ PWARN("%s", msg.c_str());
}
+
LEAVE (" sess=%p book-id=%s", this, new_book_id.c_str ());
}
@@ -352,10 +345,10 @@ QofSessionImpl::end () noexcept
{
ENTER ("sess=%p book_id=%s", this, m_book_id.c_str ());
auto backend = qof_book_get_backend (m_book);
- if (backend && backend->session_end)
- (backend->session_end) (backend);
+ if (backend != nullptr)
+ backend->session_end();
clear_error ();
- m_book_id = {};
+ m_book_id.clear();
LEAVE ("sess=%p book_id=%s", this, m_book_id.c_str ());
}
@@ -368,12 +361,12 @@ QofSessionImpl::clear_error () noexcept
m_error_message = {};
/* pop the stack on the backend as well. */
- if (qof_book_get_backend (m_book))
+ if (auto backend = qof_book_get_backend (m_book))
{
- QofBackendError err;
+ QofBackendError err = ERR_BACKEND_NO_ERR;
do
- err = qof_backend_get_error (qof_book_get_backend (m_book));
- while (ERR_BACKEND_NO_ERR != err);
+ err = backend->get_error();
+ while (err != ERR_BACKEND_NO_ERR);
}
}
@@ -388,12 +381,12 @@ QofBackendError
QofSessionImpl::get_error () noexcept
{
/* if we have a local error, return that. */
- if (ERR_BACKEND_NO_ERR != m_last_err)
+ if (m_last_err != ERR_BACKEND_NO_ERR)
return m_last_err;
+ auto qof_be = qof_book_get_backend (m_book);
+ if (qof_be == nullptr) return ERR_BACKEND_NO_ERR;
- if (!qof_book_get_backend (m_book)) return ERR_BACKEND_NO_ERR;
-
- m_last_err = qof_backend_get_error (qof_book_get_backend (m_book));
+ m_last_err = qof_be->get_error();
return m_last_err;
}
@@ -431,8 +424,9 @@ QofSession::get_backend () const noexcept
std::string
QofSessionImpl::get_file_path () const noexcept
{
- if (!qof_book_get_backend (m_book)) return nullptr;
- return qof_book_get_backend (m_book)->fullpath;
+ auto backend = qof_book_get_backend (m_book);
+ if (!backend) return nullptr;
+ return backend->get_uri();
}
std::string const &
@@ -469,17 +463,14 @@ QofSessionImpl::save (QofPercentageFunc percentage_func) noexcept
{
/* if invoked as SaveAs(), then backend not yet set */
qof_book_set_backend (m_book, backend);
- backend->percentage = percentage_func;
- if (backend->sync)
+ backend->set_percentage(percentage_func);
+ backend->sync(m_book);
+ auto err = backend->get_error();
+ if (err != ERR_BACKEND_NO_ERR)
{
- (backend->sync)(backend, m_book);
- QofBackendError err {qof_backend_get_error (backend)};
- if (ERR_BACKEND_NO_ERR != err)
- {
- push_error (err, {});
- m_saving = false;
- return;
- }
+ push_error (err, {});
+ m_saving = false;
+ return;
}
/* If we got to here, then the backend saved everything
* just fine, and we are done. So return. */
@@ -499,17 +490,15 @@ QofSessionImpl::safe_save (QofPercentageFunc percentage_func) noexcept
{
auto backend = qof_book_get_backend (m_book);
if (!backend) return;
- if (!backend->safe_sync) return;
- backend->percentage = percentage_func;
- (backend->safe_sync) (backend, get_book ());
- auto err = qof_backend_get_error (qof_book_get_backend (m_book));
- auto msg = qof_backend_get_message (qof_book_get_backend (m_book));
+ backend->set_percentage(percentage_func);
+ backend->safe_sync(get_book ());
+ auto err = backend->get_error();
+ auto msg = backend->get_message();
if (err != ERR_BACKEND_NO_ERR)
{
- m_book_id = {};
- push_error (err, msg ? msg : "");
+ m_book_id = nullptr;
+ push_error (err, msg);
}
- g_free (msg);
}
void
@@ -517,9 +506,8 @@ QofSessionImpl::ensure_all_data_loaded () noexcept
{
auto backend = qof_book_get_backend (m_book);
if (!backend) return;
- if (!backend->load) return;
- backend->load(backend, get_book (), LOAD_TYPE_LOAD_ALL);
- push_error (qof_backend_get_error (backend), {});
+ backend->load(m_book, LOAD_TYPE_LOAD_ALL);
+ push_error (backend->get_error(), {});
}
void
@@ -552,7 +540,8 @@ QofSessionImpl::process_events () const noexcept
* book-closing is implemented.
*/
bool
-QofSessionImpl::export_session (QofSessionImpl & real_session, QofPercentageFunc percentage_func) noexcept
+QofSessionImpl::export_session (QofSessionImpl & real_session,
+ QofPercentageFunc percentage_func) noexcept
{
auto real_book = real_session.get_book ();
ENTER ("tmp_session=%p real_session=%p book=%p book_id=%s",
@@ -564,12 +553,11 @@ QofSessionImpl::export_session (QofSessionImpl & real_session, QofPercentageFunc
auto backend2 = qof_book_get_backend(m_book);
if (!backend2) return false;
- backend2->percentage = percentage_func;
- if (!backend2->export_fn) return true;
+ backend2->set_percentage(percentage_func);
- (backend2->export_fn)(backend2, real_book);
- auto err = qof_backend_get_error(backend2);
- if (ERR_BACKEND_NO_ERR != err)
+ backend2->export_coa(real_book);
+ auto err = backend2->get_error();
+ if (err != ERR_BACKEND_NO_ERR)
return false;
return true;
}
@@ -749,4 +737,3 @@ qof_session_get_error (QofSession * session)
if (!session) return ERR_BACKEND_NO_BACKEND;
return session->get_error();
}
-
diff --git a/src/libqof/qof/qofutil.cpp b/src/libqof/qof/qofutil.cpp
index 43d32ca..0e988b8 100644
--- a/src/libqof/qof/qofutil.cpp
+++ b/src/libqof/qof/qofutil.cpp
@@ -273,7 +273,7 @@ qof_close(void)
{
qof_query_shutdown ();
qof_object_shutdown ();
- qof_finalize_backend_libraries();
+ QofBackend::release_backends();
qof_string_cache_destroy ();
qof_log_shutdown();
}
diff --git a/src/libqof/qof/test/test-qofinstance.cpp b/src/libqof/qof/test/test-qofinstance.cpp
index 45fe87d..e9e3c6b 100644
--- a/src/libqof/qof/test/test-qofinstance.cpp
+++ b/src/libqof/qof/test/test-qofinstance.cpp
@@ -41,11 +41,86 @@ static gboolean is_called;
#define _Q "`"
#endif
+static struct
+{
+ QofInstance *m_inst = nullptr;
+ QofBackend *m_be = nullptr;
+
+ bool m_commit_called = false;
+ bool m_commit_with_err_called = false;
+ bool m_on_error_called = false;
+ bool m_on_free_called = false;
+ bool m_on_done_called = false;
+ QofBackendError m_err = ERR_BACKEND_NO_ERR;
+} commit_test;
+
+
+class MockBackend : public QofBackend
+{
+public:
+ MockBackend() : m_qof_error{ERR_BACKEND_NO_ERR} {
+ commit_test.m_be = this;
+ }
+ void session_begin(QofSession* sess, const char* book_name,
+ bool ignore_lock, bool create, bool force) override {}
+ void session_end() override {}
+ void load(QofBook*, QofBackendLoadType) override {}
+ void sync(QofBook* book) override {}
+ void safe_sync(QofBook* book) override {}
+ void begin(QofInstance* inst) override {
+ g_assert(inst);
+ g_assert(QOF_IS_INSTANCE(inst));
+ commit_test.m_inst = inst;
+ }
+ void set_error(QofBackendError err) {
+ QofBackend::set_error(err);
+ commit_test.m_err = err;
+ }
+ void commit(QofInstance* inst) override {
+ g_assert( inst );
+ g_assert( QOF_IS_INSTANCE( inst ) );
+ g_assert( commit_test.m_inst == inst );
+ g_assert( commit_test.m_be == this );
+ commit_test.m_commit_called = true;
+ set_error(m_qof_error);
+
+ }
+ void rollback(QofInstance* inst) override {}
+ void inject_error(QofBackendError err) {
+ m_qof_error = err;
+ }
+private:
+ QofBackendError m_qof_error;
+
+};
+
typedef struct
{
QofInstance *inst;
} Fixture;
+static void
+on_error(QofInstance* inst, QofBackendError err) {
+ g_assert( inst );
+ g_assert( QOF_IS_INSTANCE( inst ) );
+ g_assert( commit_test.m_err == err );
+ commit_test.m_on_error_called = true;
+}
+static void
+on_done(QofInstance* inst) {
+ g_assert( inst );
+ g_assert( QOF_IS_INSTANCE( inst ) );
+ g_assert( commit_test.m_inst == inst );
+ commit_test.m_on_done_called = true;
+}
+static void
+on_free(QofInstance* inst) {
+ g_assert( inst );
+ g_assert( QOF_IS_INSTANCE( inst ) );
+ g_assert(commit_test.m_inst == inst );
+ commit_test.m_on_free_called = true;
+}
+
/* use g_free on error_message after this function been called */
static gboolean
fatal_handler ( const char * log_domain,
@@ -423,7 +498,6 @@ mock_backend_begin( QofBackend *be, QofInstance *inst )
{
g_assert( be );
g_assert( inst );
- g_assert( be->begin == mock_backend_begin );
g_assert_cmpstr( inst->e_type, == , "test type" );
is_called = TRUE;
}
@@ -431,14 +505,12 @@ mock_backend_begin( QofBackend *be, QofInstance *inst )
static void
test_instance_begin_edit( Fixture *fixture, gconstpointer pData )
{
- QofBackend *be;
QofBook *book;
gboolean result;
/* setup */
- be = g_new0( QofBackend, 1 );
+ auto be = new MockBackend;
g_assert( be );
- qof_backend_init( be );
book = qof_book_new();
g_assert( book );
g_assert( QOF_IS_BOOK( book ) );
@@ -467,22 +539,19 @@ test_instance_begin_edit( Fixture *fixture, gconstpointer pData )
g_test_message( "Test when instance's editlevel is <= 0 and backend is set" );
result = FALSE;
- is_called = FALSE;
qof_instance_reset_editlevel( fixture->inst );
qof_instance_set_dirty_flag( fixture->inst, FALSE );
qof_instance_set_book( fixture->inst, book );
- be->begin = mock_backend_begin;
result = qof_begin_edit( fixture->inst );
g_assert( result == TRUE );
g_assert_cmpint( qof_instance_get_editlevel( fixture->inst ), == , 1 );
g_assert( qof_instance_get_dirty_flag( fixture->inst ) == FALSE );
- g_assert( is_called );
/* clean up */
qof_book_set_backend( book, NULL );
qof_book_destroy( book );
- qof_backend_destroy( be );
- g_free( be );
+ delete be;
+
}
static void
@@ -527,82 +596,16 @@ test_instance_commit_edit( Fixture *fixture, gconstpointer pData )
/* backend commit test start */
-static struct
-{
- gpointer inst;
- gpointer be;
-
- gboolean commit_called;
- gboolean commit_with_err_called;
- gboolean on_error_called;
- gboolean on_free_called;
- gboolean on_done_called;
- QofBackendError err;
-} commit_test_part2;
-
-static void
-mock_backend_commit( QofBackend *be, QofInstance *inst )
-{
- g_assert( inst );
- g_assert( be );
- g_assert( QOF_IS_INSTANCE( inst ) );
- g_assert( commit_test_part2.inst == inst );
- g_assert( commit_test_part2.be == be );
- commit_test_part2.commit_called = TRUE;
-}
-
-static void
-mock_backend_commit_with_error( QofBackend *be, QofInstance *inst )
-{
- g_assert( inst );
- g_assert( be );
- g_assert( QOF_IS_INSTANCE( inst ) );
- g_assert( commit_test_part2.inst == inst );
- g_assert( commit_test_part2.be == be );
- qof_backend_set_error( be, ERR_BACKEND_NO_HANDLER );
- commit_test_part2.err = ERR_BACKEND_NO_HANDLER;
- commit_test_part2.commit_with_err_called = TRUE;
-}
-
-
-static void
-mock_on_error( QofInstance *inst, QofBackendError be_error )
-{
- g_assert( inst );
- g_assert( QOF_IS_INSTANCE( inst ) );
- g_assert( commit_test_part2.err == be_error );
- commit_test_part2.on_error_called = TRUE;
-}
-
-static void
-mock_on_done( QofInstance *inst )
-{
- g_assert( inst );
- g_assert( QOF_IS_INSTANCE( inst ) );
- g_assert( commit_test_part2.inst == inst );
- commit_test_part2.on_done_called = TRUE;
-}
-
-static void
-mock_on_free( QofInstance *inst )
-{
- g_assert( inst );
- g_assert( QOF_IS_INSTANCE( inst ) );
- g_assert( commit_test_part2.inst == inst );
- commit_test_part2.on_free_called = TRUE;
-}
static void
test_instance_commit_edit_part2( Fixture *fixture, gconstpointer pData )
{
- QofBackend *be;
QofBook *book;
gboolean result;
/* setup */
- be = g_new0( QofBackend, 1 );
+ auto be = new MockBackend;
g_assert( be );
- qof_backend_init( be );
book = qof_book_new();
g_assert( book );
g_assert( QOF_IS_BOOK( book ) );
@@ -610,13 +613,13 @@ test_instance_commit_edit_part2( Fixture *fixture, gconstpointer pData )
/* init */
result = FALSE;
- commit_test_part2.commit_called = FALSE;
- commit_test_part2.commit_with_err_called = FALSE;
- commit_test_part2.on_error_called = FALSE;
- commit_test_part2.on_free_called = FALSE;
- commit_test_part2.on_done_called = FALSE;
- commit_test_part2.inst = fixture->inst;
- commit_test_part2.be = be;
+ commit_test.m_commit_called = false;
+ commit_test.m_commit_with_err_called = false;
+ commit_test.m_on_error_called = false;
+ commit_test.m_on_free_called = false;
+ commit_test.m_on_done_called = false;
+ commit_test.m_inst = fixture->inst;
+ commit_test.m_be = be;
qof_instance_set_dirty_flag( fixture->inst, TRUE );
g_test_message( "Test when instance's backend not set, callbacks not set" );
@@ -626,70 +629,67 @@ test_instance_commit_edit_part2( Fixture *fixture, gconstpointer pData )
g_assert( result );
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
g_assert( !qof_instance_get_infant( fixture->inst ) );
- g_assert( !commit_test_part2.commit_called );
- g_assert( !commit_test_part2.commit_with_err_called );
- g_assert( !commit_test_part2.on_error_called );
- g_assert( !commit_test_part2.on_free_called );
- g_assert( !commit_test_part2.on_done_called );
+ g_assert( !commit_test.m_commit_called );
+ g_assert( !commit_test.m_commit_with_err_called );
+ g_assert( !commit_test.m_on_error_called );
+ g_assert( !commit_test.m_on_free_called );
+ g_assert( !commit_test.m_on_done_called );
- g_test_message( "Test when instance's backend not set, do_free is true" );
+ g_test_message( "Test when instance's backend not set, do_free is false" );
qof_instance_set_destroying( fixture->inst, TRUE );
- result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
+ result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
g_assert( result );
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
- g_assert( !commit_test_part2.commit_called );
- g_assert( !commit_test_part2.commit_with_err_called );
- g_assert( !commit_test_part2.on_error_called );
- g_assert( commit_test_part2.on_free_called );
- g_assert( !commit_test_part2.on_done_called );
+ g_assert( !commit_test.m_commit_called );
+ g_assert( !commit_test.m_commit_with_err_called );
+ g_assert( !commit_test.m_on_error_called );
+ g_assert( commit_test.m_on_free_called );
+ g_assert( !commit_test.m_on_done_called );
g_test_message( "Test when instance's backend not set, do_free is false" );
qof_instance_set_destroying( fixture->inst, FALSE );
- commit_test_part2.on_free_called = FALSE;
- result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
+ commit_test.m_on_free_called = false;
+ result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
g_assert( result );
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
- g_assert( !commit_test_part2.commit_called );
- g_assert( !commit_test_part2.commit_with_err_called );
- g_assert( !commit_test_part2.on_error_called );
- g_assert( !commit_test_part2.on_free_called );
- g_assert( commit_test_part2.on_done_called );
+ g_assert( !commit_test.m_commit_called );
+ g_assert( !commit_test.m_commit_with_err_called );
+ g_assert( !commit_test.m_on_error_called );
+ g_assert( !commit_test.m_on_free_called );
+ g_assert( commit_test.m_on_done_called );
g_test_message( "Test when instance's backend is set, all cb set, no error produced" );
qof_instance_set_book( fixture->inst, book );
qof_instance_set_destroying( fixture->inst, FALSE );
- commit_test_part2.on_done_called = FALSE;
- be->commit = mock_backend_commit;
- result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
+ commit_test.m_on_done_called = false;
+ result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
g_assert( result );
g_assert( !qof_instance_get_dirty_flag( fixture->inst ) );
- g_assert( commit_test_part2.commit_called );
- g_assert( !commit_test_part2.commit_with_err_called );
- g_assert( !commit_test_part2.on_error_called );
- g_assert( !commit_test_part2.on_free_called );
- g_assert( commit_test_part2.on_done_called );
+ g_assert( commit_test.m_commit_called );
+ g_assert( !commit_test.m_commit_with_err_called );
+ g_assert( !commit_test.m_on_error_called );
+ g_assert( !commit_test.m_on_free_called );
+ g_assert( commit_test.m_on_done_called );
g_test_message( "Test when instance's backend is set, all cb set, error produced" );
- commit_test_part2.commit_called = FALSE;
- commit_test_part2.on_done_called = FALSE;
- be->commit = mock_backend_commit_with_error;
+ commit_test.m_commit_called = false;
+ commit_test.m_on_done_called = false;
+ be->inject_error(ERR_BACKEND_NO_HANDLER);
qof_instance_set_dirty_flag( fixture->inst, TRUE );
qof_instance_set_destroying( fixture->inst, TRUE );
- result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
+ result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
g_assert( !result );
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
g_assert( !qof_instance_get_destroying( fixture->inst ) );
- g_assert( !commit_test_part2.commit_called );
- g_assert( commit_test_part2.commit_with_err_called );
- g_assert( commit_test_part2.on_error_called );
- g_assert( !commit_test_part2.on_free_called );
- g_assert( !commit_test_part2.on_done_called );
+ g_assert( commit_test.m_commit_called );
+ g_assert( commit_test.m_on_error_called );
+ g_assert( !commit_test.m_on_free_called );
+ g_assert( !commit_test.m_on_done_called );
/* clean up */
qof_book_set_backend( book, NULL );
qof_book_destroy( book );
- qof_backend_destroy( be );
- g_free( be );
+ delete be;
}
/* backend commit test end */
diff --git a/src/libqof/qof/test/test-qofsession.cpp b/src/libqof/qof/test/test-qofsession.cpp
index f77a54e..863316d 100644
--- a/src/libqof/qof/test/test-qofsession.cpp
+++ b/src/libqof/qof/test/test-qofsession.cpp
@@ -35,51 +35,51 @@ static bool sync_called {false};
static bool load_error {true};
static bool hook_called {false};
static bool data_loaded {false};
+
+class MockBackend : public QofBackend
+{
+public:
+ MockBackend() = default;
+ MockBackend(const MockBackend&) = delete;
+ MockBackend(const MockBackend&&) = delete;
+ virtual ~MockBackend() = default;
+ void session_begin(QofSession*, const char*, bool, bool, bool) {}
+ void session_end() {}
+ void load(QofBook*, QofBackendLoadType);
+ void sync(QofBook*);
+ void safe_sync(QofBook*);
+ void export_coa(QofBook*);
+};
+
void example_hook (QofSession & session)
{
hook_called = true;
}
-void test_load (QofBackend * be, QofBook *, QofBackendLoadType)
+void MockBackend::load (QofBook *, QofBackendLoadType)
{
- if (load_error) be->last_err = ERR_BACKEND_NO_BACKEND;
+ if (load_error) set_error(ERR_BACKEND_NO_BACKEND);
data_loaded = true;
}
-void test_safe_sync (QofBackend *, QofBook *)
+void MockBackend::safe_sync (QofBook *)
{
safe_sync_called = true;
}
-void test_sync (QofBackend *, QofBook *)
+void MockBackend::sync (QofBook *)
{
sync_called = true;
}
-void test_export_fn (QofBackend *, QofBook * book)
+void MockBackend::export_coa(QofBook * book)
{
exported_book = book;
}
QofBackend * test_backend_factory ()
{
- QofBackend * ret = (QofBackend*) std::malloc (sizeof (QofBackend));
- ret->session_begin = nullptr;
- ret->session_end = nullptr;
- ret->destroy_backend = nullptr;
- ret->load = &test_load;
- ret->sync = &test_sync;
- ret->safe_sync = &test_safe_sync;
- ret->export_fn = &test_export_fn;
- ret->error_msg = nullptr;
- ret->fullpath = nullptr;
- ret->last_err = ERR_BACKEND_NO_ERR;
- ret->begin = nullptr;
- ret->commit = nullptr;
- ret->rollback = nullptr;
- ret->percentage = nullptr;
- ret->config_count = 0;
- return ret;
+ return new MockBackend;
}
struct MockProvider : public QofBackendProvider
commit 06af7d794f9493fc41bbcb338d0130ea8339ab3c
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Nov 28 10:24:35 2016 -0800
Remove need for gnc_dbi_safe_sync_all to be a friend of GncDbiSqlConnection.
Adds GncDbiSqlConnection::drop_indexes, changes GncDbiSqlConnection::table_operation
to use a regex-filtered table list from the DbiConn, and converts TableOpType
to a C++ enum.
diff --git a/src/backend/dbi/gnc-backend-dbi.cpp b/src/backend/dbi/gnc-backend-dbi.cpp
index 8c00ec5..cbd251e 100644
--- a/src/backend/dbi/gnc-backend-dbi.cpp
+++ b/src/backend/dbi/gnc-backend-dbi.cpp
@@ -907,25 +907,20 @@ gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
g_return_if_fail (dbi_be != nullptr);
g_return_if_fail (book != nullptr);
- ENTER ("book=%p, primary=%p", book, dbi_be->m_book);
- auto table_list = conn->m_provider->get_table_list (conn->conn(), "");
- if (!conn->table_operation (table_list, backup))
+ ENTER ("book=%p, primary=%p", book, m_book);
+ if (!conn->table_operation (TableOpType::backup))
{
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
- conn->table_operation (table_list, rollback);
+ set_error(ERR_BACKEND_SERVER_ERR);
+ conn->table_operation (TableOpType::rollback);
LEAVE ("Failed to rename tables");
return;
}
- auto index_list = conn->m_provider->get_index_list (conn->m_conn);
- for (auto index : index_list)
+ if (!conn->drop_indexes())
{
- const char* errmsg;
- conn->m_provider->drop_index (conn->m_conn, index);
- if (DBI_ERROR_NONE != dbi_conn_error (conn->m_conn, &errmsg))
- {
- qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
- conn->table_operation (table_list, rollback);
- LEAVE ("Failed to drop indexes %s", errmsg);
+ conn->table_operation (TableOpType::rollback);
+ set_error (ERR_BACKEND_SERVER_ERR);
+ set_message("Failed to drop indexes");
+ LEAVE ("Failed to drop indexes");
return;
}
}
@@ -933,12 +928,12 @@ gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
dbi_be->sync_all(book);
if (qof_backend_check_error (qof_be))
{
- conn->table_operation (table_list, rollback);
+ conn->table_operation (TableOpType::rollback);
LEAVE ("Failed to create new database tables");
return;
}
- conn->table_operation (table_list, drop_backup);
- LEAVE ("book=%p", book);
+ conn->table_operation (TableOpType::drop_backup);
+ LEAVE ("book=%p", m_book);
}
/* ================================================================= */
static void
diff --git a/src/backend/dbi/gnc-backend-dbi.hpp b/src/backend/dbi/gnc-backend-dbi.hpp
index 0a571d1..796c825 100644
--- a/src/backend/dbi/gnc-backend-dbi.hpp
+++ b/src/backend/dbi/gnc-backend-dbi.hpp
@@ -49,14 +49,14 @@ class GncSqlRow;
* @var rollback drop the name table if it exists and rename name_back to name
* @var drop_backup Drop the backup table
*/
-typedef enum
+enum TableOpType
{
drop = 0,
empty,
backup,
rollback,
drop_backup
-} TableOpType;
+};
/**
* Return values from conn_test_dbi_library
diff --git a/src/backend/dbi/gnc-dbisqlconnection.cpp b/src/backend/dbi/gnc-dbisqlconnection.cpp
index 8731bdb..e39dc8f 100644
--- a/src/backend/dbi/gnc-dbisqlconnection.cpp
+++ b/src/backend/dbi/gnc-dbisqlconnection.cpp
@@ -28,6 +28,10 @@ extern "C"
#include <platform.h>
#include <gnc-locale-utils.h>
}
+
+#include <string>
+#include <regex>
+
#include "gnc-dbisqlconnection.hpp"
static QofLogModule log_module = G_LOG_DOMAIN;
@@ -586,16 +590,16 @@ GncDbiSqlConnection::table_manage_backup (const std::string& table_name,
dbi_result result = nullptr;
switch (op)
{
- case backup:
+ case TableOpType::backup:
result = dbi_conn_queryf (m_conn, "ALTER TABLE %s RENAME TO %s",
table_name.c_str(), new_name.c_str());
break;
- case rollback:
+ case TableOpType::rollback:
result = dbi_conn_queryf (m_conn,
"ALTER TABLE %s RENAME TO %s",
new_name.c_str(), table_name.c_str());
break;
- case drop_backup:
+ case TableOpType::drop_backup:
result = dbi_conn_queryf (m_conn, "DROP TABLE %s",
new_name.c_str());
break;
@@ -630,16 +634,20 @@ GncDbiSqlConnection::table_manage_backup (const std::string& table_name,
*/
bool
-GncDbiSqlConnection::table_operation(const StrVec& table_names,
- TableOpType op) noexcept
+GncDbiSqlConnection::table_operation(TableOpType op) noexcept
{
- g_return_val_if_fail (!table_names.empty(), FALSE);
+ static const std::regex backupre (".*_back");
bool retval{true};
- for (auto table : table_names)
+ for (auto table : m_provider->get_table_list(m_conn, ""))
{
dbi_result result;
- /* Ignore the lock table */
- if (table == lock_table)
+ /* Skip the lock table and existing backup tables; the former we don't
+ * want to touch, the latter are handled by table_manage_backup. It
+ * would be nicer to handle this with the get_table_list query, but that
+ * can accept only SQL LIKE patterns (not even regexps) and there's no
+ * way to have a negative one.
+ */
+ if (table == lock_table || std::regex_match(table, backupre))
{
continue;
}
@@ -660,7 +668,7 @@ GncDbiSqlConnection::table_operation(const StrVec& table_names,
break;
}
}
- /* Fall through */
+ /* Fall through to rename the _back tables back.*/
case backup:
case drop_backup:
result = table_manage_backup (table, op);
@@ -689,6 +697,23 @@ GncDbiSqlConnection::table_operation(const StrVec& table_names,
return retval;
}
+bool
+GncDbiSqlConnection::drop_indexes() noexcept
+{
+ auto index_list = m_provider->get_index_list (m_conn);
+ for (auto index : index_list)
+ {
+ const char* errmsg;
+ m_provider->drop_index (m_conn, index);
+ if (DBI_ERROR_NONE != dbi_conn_error (m_conn, &errmsg))
+ {
+ PERR("Failed to drop indexes %s", errmsg);
+ return false;
+ }
+ }
+ return true;
+}
+
std::string
GncDbiSqlConnection::add_columns_ddl(const std::string& table_name,
const ColVec& info_vec) const noexcept
diff --git a/src/backend/dbi/gnc-dbisqlconnection.hpp b/src/backend/dbi/gnc-dbisqlconnection.hpp
index 466e5d9..05973c8 100644
--- a/src/backend/dbi/gnc-dbisqlconnection.hpp
+++ b/src/backend/dbi/gnc-dbisqlconnection.hpp
@@ -81,12 +81,10 @@ public:
bool verify() noexcept override;
bool retry_connection(const char* msg) noexcept override;
dbi_result table_manage_backup(const std::string& table_name, TableOpType op);
- bool table_operation (const StrVec& table_name_list,
- TableOpType op) noexcept;
+ bool table_operation (TableOpType op) noexcept;
std::string add_columns_ddl(const std::string& table_name,
const ColVec& info_vec) const noexcept;
- friend void gnc_dbi_safe_sync_all (QofBackend* qbe, QofBook* book);
-
+ bool drop_indexes() noexcept;
private:
QofBackend* m_qbe;
dbi_conn m_conn;
commit 46ce3f3745dd69dc89f055cb2bf3710c9ba3d6aa
Author: John Ralls <jralls at ceridwen.us>
Date: Sun Nov 27 14:12:43 2016 -0800
Rename qofbackend-p.h and qofbackend.cpp
To qof-backend.hpp and qof-backend.cpp respectively, to reflect that they
implement the QofBackend class (which won’t be a class until a future commit).
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0adb5c1..dc5145c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -482,7 +482,7 @@ src/libqof/qof/gnc-timezone.cpp
src/libqof/qof/guid.cpp
src/libqof/qof/kvp_frame.cpp
src/libqof/qof/kvp-value.cpp
-src/libqof/qof/qofbackend.cpp
+src/libqof/qof/qof-backend.cpp
src/libqof/qof/qofbook.cpp
src/libqof/qof/qofchoice.cpp
src/libqof/qof/qofclass.cpp
diff --git a/src/backend/sql/gnc-sql-backend.hpp b/src/backend/sql/gnc-sql-backend.hpp
index 3146960..1ad4608 100644
--- a/src/backend/sql/gnc-sql-backend.hpp
+++ b/src/backend/sql/gnc-sql-backend.hpp
@@ -27,13 +27,14 @@
extern "C"
{
#include <qof.h>
-#include <qofbackend-p.h>
#include <Account.h>
}
#include <memory>
#include <exception>
#include <sstream>
#include <vector>
+#include <qof-backend.hpp>
+
class GncSqlColumnTableEntry;
using GncSqlColumnTableEntryPtr = std::shared_ptr<GncSqlColumnTableEntry>;
using EntryVec = std::vector<GncSqlColumnTableEntryPtr>;
diff --git a/src/backend/sql/gnc-sql-column-table-entry.hpp b/src/backend/sql/gnc-sql-column-table-entry.hpp
index 26b9271..0dceddf 100644
--- a/src/backend/sql/gnc-sql-column-table-entry.hpp
+++ b/src/backend/sql/gnc-sql-column-table-entry.hpp
@@ -27,7 +27,6 @@
extern "C"
{
#include <qof.h>
-#include "qofbackend-p.h"
}
#include <memory>
#include <vector>
diff --git a/src/backend/xml/gnc-backend-xml.cpp b/src/backend/xml/gnc-backend-xml.cpp
index 6ccd0df..7c4088b 100644
--- a/src/backend/xml/gnc-backend-xml.cpp
+++ b/src/backend/xml/gnc-backend-xml.cpp
@@ -78,7 +78,7 @@ extern "C"
#include <gnc-backend-prov.hpp>
#include "gnc-backend-xml.h"
-#include <qofbackend-p.h>
+#include <qof-backend.hpp>
#include "gnc-xml-backend.hpp"
#include "gnc-xml-helper.h"
#include "io-gncxml-v2.h"
diff --git a/src/backend/xml/gnc-backend-xml.h b/src/backend/xml/gnc-backend-xml.h
index f82bc25..a9532d0 100644
--- a/src/backend/xml/gnc-backend-xml.h
+++ b/src/backend/xml/gnc-backend-xml.h
@@ -36,7 +36,6 @@ extern "C"
#endif
#include <qof.h>
#include <gmodule.h>
-#include <qofbackend-p.h>
typedef enum
{
@@ -70,6 +69,6 @@ void qof_backend_module_init (void);
#endif
#ifdef __cplusplus
}
-
+#include <qof-backend.hpp>
#endif
#endif /* GNC_BACKEND_XML_H_ */
diff --git a/src/backend/xml/gnc-xml-backend.hpp b/src/backend/xml/gnc-xml-backend.hpp
index ab8c64b..e49486f 100644
--- a/src/backend/xml/gnc-xml-backend.hpp
+++ b/src/backend/xml/gnc-xml-backend.hpp
@@ -21,10 +21,10 @@
extern "C"
{
#include <qof.h>
-#include <qofbackend-p.h>
}
#include <string>
+#include <qof-backend.hpp>
class GncXmlBackend
{
diff --git a/src/engine/test/utest-Transaction.cpp b/src/engine/test/utest-Transaction.cpp
index dcdac4d..0e06a34 100644
--- a/src/engine/test/utest-Transaction.cpp
+++ b/src/engine/test/utest-Transaction.cpp
@@ -35,7 +35,6 @@ extern "C"
#include "../gnc-lot.h"
#include "../gnc-event.h"
#include <qof.h>
-#include <qofbackend-p.h>
#ifdef HAVE_GLIB_2_38
#define _Q "'"
@@ -50,6 +49,7 @@ static const gchar *suitename = "/engine/Transaction";
void test_suite_transaction ( void );
}
+#include <qof-backend.hpp>
#include <kvp_frame.hpp>
/* Copied from Transaction.c. Changing these values will break
diff --git a/src/libqof/CMakeLists.txt b/src/libqof/CMakeLists.txt
index 90aa60e..05a03da 100644
--- a/src/libqof/CMakeLists.txt
+++ b/src/libqof/CMakeLists.txt
@@ -14,7 +14,7 @@ SET (gnc_qof_HEADERS
qof/kvp_frame.hpp
qof/kvp-value.hpp
qof/qof.h
- qof/qofbackend-p.h
+ qof/qof-backend.hpp
qof/qofbackend.h
qof/qofbook.h
qof/qofbookslots.h
@@ -59,7 +59,7 @@ SET (gnc_qof_SOURCES
qof/guid.cpp
qof/kvp_frame.cpp
qof/kvp-value.cpp
- qof/qofbackend.cpp
+ qof/qof-backend.cpp
qof/qofbook.cpp
qof/qofchoice.cpp
qof/qofclass.cpp
diff --git a/src/libqof/qof/Makefile.am b/src/libqof/qof/Makefile.am
index 5e83159..c2fec7f 100644
--- a/src/libqof/qof/Makefile.am
+++ b/src/libqof/qof/Makefile.am
@@ -32,7 +32,7 @@ libgnc_qof_la_SOURCES = \
guid.cpp \
kvp_frame.cpp \
kvp-value.cpp \
- qofbackend.cpp \
+ qof-backend.cpp \
qofbook.cpp \
qofchoice.cpp \
qofclass.cpp \
@@ -61,7 +61,7 @@ qofinclude_HEADERS = \
kvp_frame.hpp \
kvp-value.hpp \
qof.h \
- qofbackend-p.h \
+ qof-backend.hpp \
qofbackend.h \
qofbook.h \
qofbookslots.h \
diff --git a/src/libqof/qof/qofbackend.cpp b/src/libqof/qof/qof-backend.cpp
similarity index 99%
rename from src/libqof/qof/qofbackend.cpp
rename to src/libqof/qof/qof-backend.cpp
index 6613fed..28feadc 100644
--- a/src/libqof/qof/qofbackend.cpp
+++ b/src/libqof/qof/qof-backend.cpp
@@ -34,10 +34,10 @@ extern "C"
#include <gmodule.h>
#include <errno.h>
#include "qof.h"
-#include "qofbackend-p.h"
-
}
+#include "qof-backend.hpp"
+
G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_BACKEND;
#define QOF_CONFIG_DESC "desc"
diff --git a/src/libqof/qof/qofbackend-p.h b/src/libqof/qof/qof-backend.hpp
similarity index 100%
rename from src/libqof/qof/qofbackend-p.h
rename to src/libqof/qof/qof-backend.hpp
diff --git a/src/libqof/qof/qofbook.cpp b/src/libqof/qof/qofbook.cpp
index e7701f6..270fc8b 100644
--- a/src/libqof/qof/qofbook.cpp
+++ b/src/libqof/qof/qofbook.cpp
@@ -52,7 +52,7 @@ extern "C"
#include "qof.h"
#include "qofevent-p.h"
-#include "qofbackend-p.h"
+#include "qofbackend.h"
#include "qofbook-p.h"
#include "qofid-p.h"
#include "qofobject-p.h"
diff --git a/src/libqof/qof/qofinstance.cpp b/src/libqof/qof/qofinstance.cpp
index 9f3f542..5e923ea 100644
--- a/src/libqof/qof/qofinstance.cpp
+++ b/src/libqof/qof/qofinstance.cpp
@@ -41,6 +41,7 @@ extern "C"
#include "qofid-p.h"
#include "kvp_frame.hpp"
#include "qofinstance-p.h"
+#include "qof-backend.hpp"
static QofLogModule log_module = QOF_MOD_ENGINE;
diff --git a/src/libqof/qof/qofquery.cpp b/src/libqof/qof/qofquery.cpp
index c6cad34..1fac61c 100644
--- a/src/libqof/qof/qofquery.cpp
+++ b/src/libqof/qof/qofquery.cpp
@@ -33,7 +33,7 @@ extern "C"
}
#include "qof.h"
-#include "qofbackend-p.h"
+#include "qof-backend.hpp"
#include "qofbook-p.h"
#include "qofclass-p.h"
#include "qofquery-p.h"
diff --git a/src/libqof/qof/qofsession.cpp b/src/libqof/qof/qofsession.cpp
index cf9c31f..91e04c6 100644
--- a/src/libqof/qof/qofsession.cpp
+++ b/src/libqof/qof/qofsession.cpp
@@ -56,7 +56,7 @@ extern "C"
static QofLogModule log_module = QOF_MOD_SESSION;
} //extern 'C'
-#include "qofbackend-p.h"
+#include "qof-backend.hpp"
#include "qofsession.hpp"
#include "gnc-backend-prov.hpp"
diff --git a/src/libqof/qof/qofutil.cpp b/src/libqof/qof/qofutil.cpp
index 2881fd7..43d32ca 100644
--- a/src/libqof/qof/qofutil.cpp
+++ b/src/libqof/qof/qofutil.cpp
@@ -32,7 +32,7 @@
#include <stdlib.h>
#include <string.h>
#include "qof.h"
-#include "qofbackend-p.h"
+#include "qof-backend.hpp"
G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_UTIL;
diff --git a/src/libqof/qof/test/Makefile.am b/src/libqof/qof/test/Makefile.am
index f5f6507..5c43d20 100644
--- a/src/libqof/qof/test/Makefile.am
+++ b/src/libqof/qof/test/Makefile.am
@@ -14,9 +14,7 @@ test_qof_SOURCES = \
test-qofbook.c \
test-qofinstance.cpp \
test-qofobject.c \
- test-qofsession-old.cpp \
test-qof-string-cache.c \
- test-gnc-guid-old.cpp \
${top_srcdir}/src/test-core/unittest-support.c
test_qof_HEADERS = \
diff --git a/src/libqof/qof/test/test-qofinstance.cpp b/src/libqof/qof/test/test-qofinstance.cpp
index 955be9c..45fe87d 100644
--- a/src/libqof/qof/test/test-qofinstance.cpp
+++ b/src/libqof/qof/test/test-qofinstance.cpp
@@ -27,8 +27,8 @@ extern "C"
#include <glib.h>
#include <unittest-support.h>
#include "../qof.h"
-#include "../qofbackend-p.h"
}
+#include "../qof-backend.hpp"
#include "../kvp_frame.hpp"
static const gchar *suitename = "/qof/qofinstance";
extern "C" void test_suite_qofinstance ( void );
diff --git a/src/libqof/qof/test/test-qofsession.cpp b/src/libqof/qof/test/test-qofsession.cpp
index 7a30cde..f77a54e 100644
--- a/src/libqof/qof/test/test-qofsession.cpp
+++ b/src/libqof/qof/test/test-qofsession.cpp
@@ -25,7 +25,7 @@
#include <gtest/gtest.h>
#include "../guid.hpp"
#include <qofsession.hpp>
-#include "qofbackend-p.h"
+#include <qof-backend.hpp>
#include <cstdlib>
#include "../gnc-backend-prov.hpp"
commit eace6250075890a13975280cf0d2af0804466ff8
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Nov 21 09:55:39 2016 -0800
Extract class GncXmlBackend from gnc-backend-xml.cpp to gnu-xml-backend.cpp.
diff --git a/src/backend/xml/CMakeLists.txt b/src/backend/xml/CMakeLists.txt
index 26f4642..3094bec 100644
--- a/src/backend/xml/CMakeLists.txt
+++ b/src/backend/xml/CMakeLists.txt
@@ -20,6 +20,7 @@ SET (backend_xml_utils_noinst_HEADERS
gnc-owner-xml-v2.h
gnc-tax-table-xml-v2.h
gnc-vendor-xml-v2.h
+ gnc-xml-backend.hpp
gnc-xml-helper.h
io-example-account.h
io-gncxml-gen.h
@@ -57,6 +58,7 @@ SET (backend_xml_utils_SOURCES
gnc-tax-table-xml-v2.cpp
gnc-transaction-xml-v2.cpp
gnc-vendor-xml-v2.cpp
+ gnc-xml-backend.cpp
gnc-xml-helper.cpp
io-example-account.cpp
io-gncxml-gen.cpp
diff --git a/src/backend/xml/Makefile.am b/src/backend/xml/Makefile.am
index e84e589..e64da12 100644
--- a/src/backend/xml/Makefile.am
+++ b/src/backend/xml/Makefile.am
@@ -41,6 +41,7 @@ libgnc_backend_xml_utils_la_SOURCES = \
gnc-tax-table-xml-v2.cpp \
gnc-transaction-xml-v2.cpp \
gnc-vendor-xml-v2.cpp \
+ gnc-xml-backend.cpp \
gnc-xml-helper.cpp \
io-example-account.cpp \
io-gncxml-gen.cpp \
@@ -71,6 +72,7 @@ noinst_HEADERS = \
gnc-owner-xml-v2.h \
gnc-tax-table-xml-v2.h \
gnc-vendor-xml-v2.h \
+ gnc-xml-backend.hpp \
gnc-xml-helper.h \
io-example-account.h \
io-gncxml-gen.h \
diff --git a/src/backend/xml/gnc-backend-xml.cpp b/src/backend/xml/gnc-backend-xml.cpp
index ab9e353..6ccd0df 100644
--- a/src/backend/xml/gnc-backend-xml.cpp
+++ b/src/backend/xml/gnc-backend-xml.cpp
@@ -31,10 +31,6 @@ extern "C"
{
#include "config.h"
-#include <platform.h>
-#if PLATFORM(WINDOWS)
-#include <windows.h>
-#endif
#include <glib.h>
#include <glib/gi18n.h>
@@ -43,9 +39,7 @@ extern "C"
#include <locale.h>
#include <fcntl.h>
#include <limits.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <regex.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#else
@@ -73,10 +67,8 @@ extern "C"
#endif
#include "qof.h"
-#include "TransLog.h"
#include "gnc-engine.h"
-
-#include "gnc-uri-utils.h"
+#include <gnc-uri-utils.h>
#include "gnc-prefs.h"
#ifndef HAVE_STRPTIME
@@ -87,6 +79,7 @@ extern "C"
#include <gnc-backend-prov.hpp>
#include "gnc-backend-xml.h"
#include <qofbackend-p.h>
+#include "gnc-xml-backend.hpp"
#include "gnc-xml-helper.h"
#include "io-gncxml-v2.h"
#include "io-gncxml.h"
@@ -105,7 +98,6 @@ extern "C"
static QofLogModule log_module = GNC_MOD_BACKEND;
-static gboolean save_may_clobber_data (XmlBackend *xml_be);
struct QofXmlBackendProvider : public QofBackendProvider
{
@@ -121,291 +113,16 @@ struct QofXmlBackendProvider : public QofBackendProvider
};
-/* ================================================================= */
-
-static gboolean
-gnc_xml_be_get_file_lock (XmlBackend* xml_be)
-{
- struct stat statbuf;
-#ifndef G_OS_WIN32
- char* pathbuf = NULL, *path = NULL, *tmpbuf = NULL;
- size_t pathbuf_size = 0;
-#endif
- int rc;
- QofBackendError be_err;
-
- rc = g_stat (xml_be->m_lockfile, &statbuf);
- if (!rc)
- {
- /* oops .. file is locked by another user .. */
- qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
- return FALSE;
- }
-
- xml_be->m_lockfd = g_open (xml_be->m_lockfile, O_RDWR | O_CREAT | O_EXCL ,
- S_IRUSR | S_IWUSR);
- if (xml_be->m_lockfd < 0)
- {
- /* oops .. we can't create the lockfile .. */
- switch (errno)
- {
- case EACCES:
- case EROFS:
- case ENOSPC:
- PWARN ("Unable to create the lockfile %s; may not have write priv",
- xml_be->m_lockfile);
- be_err = ERR_BACKEND_READONLY;
- break;
- default:
- be_err = ERR_BACKEND_LOCKED;
- break;
- }
- qof_backend_set_error ((QofBackend*)xml_be, be_err);
- return FALSE;
- }
-
- /* OK, now work around some NFS atomic lock race condition
- * mumbo-jumbo. We do this by linking a unique file, and
- * then examining the link count. At least that's what the
- * NFS programmers guide suggests.
- * Note: the "unique filename" must be unique for the
- * triplet filename-host-process, otherwise accidental
- * aliases can occur.
- */
-
- /* apparently, even this code may not work for some NFS
- * implementations. In the long run, I am told that
- * ftp.debian.org
- * /pub/debian/dists/unstable/main/source/libs/liblockfile_0.1-6.tar.gz
- * provides a better long-term solution.
- */
-
-#ifndef G_OS_WIN32
- pathbuf_size = strlen (xml_be->m_lockfile) + 100;
- pathbuf = (char*) malloc (pathbuf_size);
- if (pathbuf == NULL)
- {
- return FALSE;
- }
- strcpy (pathbuf, xml_be->m_lockfile);
- path = strrchr (pathbuf, '.');
- while (snprintf (path, pathbuf_size - (path - pathbuf), ".%lx.%d.LNK",
- gethostid (), getpid ())
- >= static_cast<int> (pathbuf_size - (path - pathbuf)))
- {
- pathbuf_size += 100;
- tmpbuf = (char*) realloc (pathbuf, pathbuf_size);
- if (tmpbuf == NULL)
- {
- free (pathbuf);
- return FALSE;
- }
- else
- {
- pathbuf = tmpbuf;
- }
- }
-
- rc = link (xml_be->m_lockfile, pathbuf);
- if (rc)
- {
- /* If hard links aren't supported, just allow the lock. */
- if (errno == EPERM || errno == ENOSYS
-# ifdef EOPNOTSUPP
- || errno == EOPNOTSUPP
-# endif
-# ifdef ENOTSUP
- || errno == ENOTSUP
-# endif
- )
- {
- xml_be->m_linkfile = NULL;
- free (pathbuf);
- return TRUE;
- }
-
- /* Otherwise, something else is wrong. */
- qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
- g_unlink (pathbuf);
- free (pathbuf);
- close (xml_be->m_lockfd);
- g_unlink (xml_be->m_lockfile);
- return FALSE;
- }
-
- rc = g_stat (xml_be->m_lockfile, &statbuf);
- if (rc)
- {
- /* oops .. stat failed! This can't happen! */
- qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
- qof_backend_set_message ((QofBackend*)xml_be, "Failed to stat lockfile %s",
- xml_be->m_lockfile);
- g_unlink (pathbuf);
- free (pathbuf);
- close (xml_be->m_lockfd);
- g_unlink (xml_be->m_lockfile);
- return FALSE;
- }
-
- if (statbuf.st_nlink != 2)
- {
- qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
- g_unlink (pathbuf);
- free (pathbuf);
- close (xml_be->m_lockfd);
- g_unlink (xml_be->m_lockfile);
- return FALSE;
- }
-
- xml_be->m_linkfile = g_strdup (pathbuf);
- free (pathbuf);
-
- return TRUE;
-
-#else /* ifndef G_OS_WIN32 */
- /* On windows, there is no NFS and the open(,O_CREAT | O_EXCL)
- is sufficient for locking. */
- xml_be->m_linkfile = NULL;
- return TRUE;
-#endif /* ifndef G_OS_WIN32 */
-}
-
-/* ================================================================= */
-#define XML_URI_PREFIX "xml://"
-#define FILE_URI_PREFIX "file://"
static void
xml_session_begin (QofBackend* qof_be, QofSession* session,
const char* book_id, gboolean ignore_lock,
gboolean create, gboolean force)
{
- XmlBackend* xml_be = (XmlBackend*) qof_be;
+ GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
ENTER (" ");
-
- /* Make sure the directory is there */
- xml_be->m_fullpath = gnc_uri_get_path (book_id);
-
- if (NULL == xml_be->m_fullpath)
- {
- qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (qof_be, "No path specified");
- LEAVE ("");
- return;
- }
- if (create && !force && save_may_clobber_data (xml_be))
- {
- qof_backend_set_error (qof_be, ERR_BACKEND_STORE_EXISTS);
- LEAVE ("Might clobber, no force");
- return;
- }
-
- xml_be->be.fullpath = xml_be->m_fullpath;
- xml_be->m_dirname = g_path_get_dirname (xml_be->m_fullpath);
-
- {
- struct stat statbuf;
- int rc;
-
- /* Again check whether the directory can be accessed */
- rc = g_stat (xml_be->m_dirname, &statbuf);
- if (rc != 0
-#if COMPILER(MSVC)
- || (statbuf.st_mode & _S_IFDIR) == 0
-#else
- || !S_ISDIR (statbuf.st_mode)
-#endif
- )
- {
- /* Error on stat or if it isn't a directory means we
- cannot find this filename */
- qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (qof_be, "Couldn't find directory for %s",
- xml_be->m_fullpath);
- PWARN ("Couldn't find directory for %s", xml_be->m_fullpath);
- g_free (xml_be->m_fullpath);
- xml_be->m_fullpath = NULL;
- g_free (xml_be->m_dirname);
- xml_be->m_dirname = NULL;
- LEAVE ("");
- return;
- }
-
- /* Now check whether we can g_stat the file itself */
- rc = g_stat (xml_be->m_fullpath, &statbuf);
- if ((rc != 0) && (!create))
- {
- /* Error on stat means the file doesn't exist */
- qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (qof_be, "Couldn't find %s",
- xml_be->m_fullpath);
- PWARN ("Couldn't find %s", xml_be->m_fullpath);
- g_free (xml_be->m_fullpath);
- xml_be->m_fullpath = NULL;
- g_free (xml_be->m_dirname);
- xml_be->m_dirname = NULL;
- LEAVE ("");
- return;
- }
- if (rc == 0
-#if COMPILER(MSVC)
- && (statbuf.st_mode & _S_IFDIR) != 0
-#else
- && S_ISDIR (statbuf.st_mode)
-#endif
- )
- {
- qof_backend_set_error (qof_be, ERR_FILEIO_UNKNOWN_FILE_TYPE);
- qof_backend_set_message (qof_be, "Path %s is a directory",
- xml_be->m_fullpath);
- PWARN ("Path %s is a directory", xml_be->m_fullpath);
- g_free (xml_be->m_fullpath);
- xml_be->m_fullpath = NULL;
- g_free (xml_be->m_dirname);
- xml_be->m_dirname = NULL;
- LEAVE ("");
- return;
- }
- }
-
-
- /* ---------------------------------------------------- */
- /* We should now have a fully resolved path name.
- * Let's start logging */
- xaccLogSetBaseName (xml_be->m_fullpath);
- PINFO ("logpath=%s", xml_be->m_fullpath ? xml_be->m_fullpath : "(null)");
-
- /* And let's see if we can get a lock on it. */
- xml_be->m_lockfile = g_strconcat (xml_be->m_fullpath, ".LCK", NULL);
-
- if (!ignore_lock && !gnc_xml_be_get_file_lock (xml_be))
- {
- // We should not ignore the lock, but couldn't get it. The
- // be_get_file_lock() already set the appropriate backend_error in this
- // case, so we just return here.
- g_free (xml_be->m_lockfile);
- xml_be->m_lockfile = NULL;
-
- if (force)
- {
- QofBackendError berror = qof_backend_get_error (qof_be);
- if (berror == ERR_BACKEND_LOCKED || berror == ERR_BACKEND_READONLY)
- {
- // Even though we couldn't get the lock, we were told to force
- // the opening. This is ok because the FORCE argument is
- // changed only if the caller wants a read-only book.
- }
- else
- {
- // Unknown error. Push it again on the error stack.
- qof_backend_set_error (qof_be, berror);
- }
- }
-
- LEAVE ("");
- return;
- }
-
+ xml_be->session_begin(session, book_id, ignore_lock, create, force);
LEAVE (" ");
return;
}
@@ -415,204 +132,16 @@ xml_session_begin (QofBackend* qof_be, QofSession* session,
static void
xml_session_end (QofBackend* qof_be)
{
- XmlBackend* xml_be = (XmlBackend*)qof_be;
+ GncXmlBackend* xml_be = (GncXmlBackend*)qof_be;
ENTER (" ");
-
- if (xml_be->m_book && qof_book_is_readonly (xml_be->m_book))
- {
- qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
- return;
- }
-
- if (xml_be->m_linkfile)
- g_unlink (xml_be->m_linkfile);
-
- if (xml_be->m_lockfd > 0)
- close (xml_be->m_lockfd);
-
- if (xml_be->m_lockfile)
- {
- int rv;
-#ifdef G_OS_WIN32
- /* On windows, we need to allow write-access before
- g_unlink() can succeed */
- rv = g_chmod (xml_be->m_lockfile, S_IWRITE | S_IREAD);
-#endif
- rv = g_unlink (xml_be->m_lockfile);
- if (rv)
- {
- PWARN ("Error on g_unlink(%s): %d: %s", xml_be->m_lockfile,
- errno, g_strerror (errno) ? g_strerror (errno) : "");
- }
- }
-
- g_free (xml_be->m_dirname);
- xml_be->m_dirname = NULL;
-
- g_free (xml_be->m_fullpath);
- xml_be->m_fullpath = NULL;
-
- g_free (xml_be->m_lockfile);
- xml_be->m_lockfile = NULL;
-
- g_free (xml_be->m_linkfile);
- xml_be->m_linkfile = NULL;
+ xml_be->session_end();
LEAVE (" ");
}
static void
xml_destroy_backend (QofBackend* qof_be)
{
- /* Stop transaction logging */
- xaccLogSetBaseName (NULL);
-
- qof_backend_destroy (qof_be);
- g_free (qof_be);
-}
-
-/* ================================================================= */
-/* Write the financial data in a book to a file, returning FALSE on
- error and setting the error_result to indicate what went wrong if
- it's not NULL. This function does not manage file locks in any
- way.
-
- If make_backup is true, write out a time-stamped copy of the file
- into the same directory as the indicated file, with a filename of
- "file.YYYYMMDDHHMMSS.gnucash" where YYYYMMDDHHMMSS is replaced with the
- current year/month/day/hour/minute/second. */
-
-/* The variable buf_size must be a compile-time constant */
-#define buf_size 1024
-
-static gboolean
-copy_file (const char* orig, const char* bkup)
-{
- char buf[buf_size];
- int orig_fd;
- int bkup_fd;
- int flags = 0;
- ssize_t count_write;
- ssize_t count_read;
-
-#ifdef G_OS_WIN32
- flags = O_BINARY;
-#endif
-
- orig_fd = g_open (orig, O_RDONLY | flags, 0);
- if (orig_fd == -1)
- {
- return FALSE;
- }
- bkup_fd = g_open (bkup, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | flags, 0600);
- if (bkup_fd == -1)
- {
- close (orig_fd);
- return FALSE;
- }
-
- do
- {
- count_read = read (orig_fd, buf, buf_size);
- if (count_read == -1 && errno != EINTR)
- {
- close (orig_fd);
- close (bkup_fd);
- return FALSE;
- }
-
- if (count_read > 0)
- {
- count_write = write (bkup_fd, buf, count_read);
- if (count_write == -1)
- {
- close (orig_fd);
- close (bkup_fd);
- return FALSE;
- }
- }
- }
- while (count_read > 0);
-
- close (orig_fd);
- close (bkup_fd);
-
- return TRUE;
-}
-
-/* ================================================================= */
-
-static gboolean
-gnc_int_link_or_make_backup (XmlBackend* xml_be, const char* orig,
- const char* bkup)
-{
- gboolean copy_success = FALSE;
- int err_ret =
-#ifdef HAVE_LINK
- link (orig, bkup)
-#else
- - 1
-#endif
- ;
- if (err_ret != 0)
- {
-#ifdef HAVE_LINK
- if (errno == EPERM || errno == ENOSYS
-# ifdef EOPNOTSUPP
- || errno == EOPNOTSUPP
-# endif
-# ifdef ENOTSUP
- || errno == ENOTSUP
-# endif
-# ifdef ENOSYS
- || errno == ENOSYS
-# endif
- )
-#endif
- {
- copy_success = copy_file (orig, bkup);
- }
-
- if (!copy_success)
- {
- qof_backend_set_error ((QofBackend*)xml_be, ERR_FILEIO_BACKUP_ERROR);
- PWARN ("unable to make file backup from %s to %s: %s",
- orig, bkup, g_strerror (errno) ? g_strerror (errno) : "");
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/* ================================================================= */
-
-static QofBookFileType
-gnc_xml_be_determine_file_type (const char* path)
-{
- gboolean with_encoding;
- QofBookFileType v2type;
-
- v2type = gnc_is_xml_data_file_v2 (path, &with_encoding);
- if (v2type == GNC_BOOK_XML2_FILE)
- {
- if (with_encoding)
- {
- return GNC_BOOK_XML2_FILE;
- }
- else
- {
- return GNC_BOOK_XML2_FILE_NO_ENCODING;
- }
- }
- else if (v2type == GNC_BOOK_POST_XML2_0_0_FILE)
- {
- return GNC_BOOK_POST_XML2_0_0_FILE;
- }
- else if (v2type == GNC_BOOK_XML1_FILE)
- {
- return GNC_BOOK_XML1_FILE;
- }
- return GNC_BOOK_NOT_OURS;
+ delete reinterpret_cast<GncXmlBackend*>(qof_be);
}
bool
@@ -672,449 +201,30 @@ det_exit:
return result;
}
-static gboolean
-gnc_xml_be_backup_file (XmlBackend* xml_be)
-{
- gboolean bkup_ret;
- char* timestamp;
- char* backup;
- const char* datafile;
- struct stat statbuf;
- int rc;
-
- datafile = xml_be->m_fullpath;
-
- rc = g_stat (datafile, &statbuf);
- if (rc)
- return (errno == ENOENT);
-
- if (gnc_xml_be_determine_file_type (datafile) == GNC_BOOK_BIN_FILE)
- {
- /* make a more permanent safer backup */
- const char* back = "-binfmt.bkup";
- char* bin_bkup = g_new (char, strlen (datafile) + strlen (back) + 1);
- strcpy (bin_bkup, datafile);
- strcat (bin_bkup, back);
- bkup_ret = gnc_int_link_or_make_backup (xml_be, datafile, bin_bkup);
- g_free (bin_bkup);
- if (!bkup_ret)
- {
- return FALSE;
- }
- }
-
- timestamp = gnc_date_timestamp ();
- backup = g_strconcat (datafile, ".", timestamp, GNC_DATAFILE_EXT, NULL);
- g_free (timestamp);
-
- bkup_ret = gnc_int_link_or_make_backup (xml_be, datafile, backup);
- g_free (backup);
-
- return bkup_ret;
-}
-
-/* ================================================================= */
-
-static gboolean
-gnc_xml_be_write_to_file (XmlBackend* xml_be,
- QofBook* book,
- const gchar* datafile,
- gboolean make_backup)
-{
- QofBackend* qof_be = &xml_be->be;
- char* tmp_name;
- struct stat statbuf;
- int rc;
- QofBackendError be_err;
-
- ENTER (" book=%p file=%s", book, datafile);
-
- if (book && qof_book_is_readonly (book))
- {
- /* Are we read-only? Don't continue in this case. */
- qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
- LEAVE ("");
- return FALSE;
- }
-
- /* If the book is 'clean', recently saved, then don't save again. */
- /* XXX this is currently broken due to faulty 'Save As' logic. */
- /* if (FALSE == qof_book_session_not_saved (book)) return FALSE; */
-
- tmp_name = g_new (char, strlen (datafile) + 12);
- strcpy (tmp_name, datafile);
- strcat (tmp_name, ".tmp-XXXXXX");
-
- if (!mktemp (tmp_name))
- {
- qof_backend_set_error (qof_be, ERR_BACKEND_MISC);
- qof_backend_set_message (qof_be, "Failed to make temp file");
- LEAVE ("");
- return FALSE;
- }
-
- if (make_backup)
- {
- if (!gnc_xml_be_backup_file (xml_be))
- {
- LEAVE ("");
- return FALSE;
- }
- }
-
- if (gnc_book_write_to_xml_file_v2 (book, tmp_name,
- gnc_prefs_get_file_save_compressed ()))
- {
- /* Record the file's permissions before g_unlinking it */
- rc = g_stat (datafile, &statbuf);
- if (rc == 0)
- {
- /* We must never chmod the file /dev/null */
- g_assert (g_strcmp0 (tmp_name, "/dev/null") != 0);
-
- /* Use the permissions from the original data file */
- if (g_chmod (tmp_name, statbuf.st_mode) != 0)
- {
- /* qof_backend_set_error(qof_be, ERR_BACKEND_PERM); */
- /* qof_backend_set_message(qof_be, "Failed to chmod filename %s", tmp_name ); */
- /* Even if the chmod did fail, the save
- nevertheless completed successfully. It is
- therefore wrong to signal the ERR_BACKEND_PERM
- error here which implies that the saving itself
- failed. Instead, we simply ignore this. */
- PWARN ("unable to chmod filename %s: %s",
- tmp_name ? tmp_name : "(null)",
- g_strerror (errno) ? g_strerror (errno) : "");
-#if VFAT_DOESNT_SUCK /* chmod always fails on vfat/samba fs */
- /* g_free(tmp_name); */
- /* return FALSE; */
-#endif
- }
-#ifdef HAVE_CHOWN
- /* Don't try to change the owner. Only root can do
- that. */
- if (chown (tmp_name, -1, statbuf.st_gid) != 0)
- {
- /* qof_backend_set_error(qof_be, ERR_BACKEND_PERM); */
- /* qof_backend_set_message(qof_be, "Failed to chown filename %s", tmp_name ); */
- /* A failed chown doesn't mean that the saving itself
- failed. So don't abort with an error here! */
- PWARN ("unable to chown filename %s: %s",
- tmp_name ? tmp_name : "(null)",
- strerror (errno) ? strerror (errno) : "");
-#if VFAT_DOESNT_SUCK /* chown always fails on vfat fs */
- /* g_free(tmp_name);
- return FALSE; */
-#endif
- }
-#endif
- }
- if (g_unlink (datafile) != 0 && errno != ENOENT)
- {
- qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
- PWARN ("unable to unlink filename %s: %s",
- datafile ? datafile : "(null)",
- g_strerror (errno) ? g_strerror (errno) : "");
- g_free (tmp_name);
- LEAVE ("");
- return FALSE;
- }
- if (!gnc_int_link_or_make_backup (xml_be, tmp_name, datafile))
- {
- qof_backend_set_error (qof_be, ERR_FILEIO_BACKUP_ERROR);
- qof_backend_set_message (qof_be, "Failed to make backup file %s",
- datafile ? datafile : "NULL");
- g_free (tmp_name);
- LEAVE ("");
- return FALSE;
- }
- if (g_unlink (tmp_name) != 0)
- {
- qof_backend_set_error (qof_be, ERR_BACKEND_PERM);
- PWARN ("unable to unlink temp filename %s: %s",
- tmp_name ? tmp_name : "(null)",
- g_strerror (errno) ? g_strerror (errno) : "");
- g_free (tmp_name);
- LEAVE ("");
- return FALSE;
- }
- g_free (tmp_name);
-
- /* Since we successfully saved the book,
- * we should mark it clean. */
- qof_book_mark_session_saved (book);
- LEAVE (" successful save of book=%p to file=%s", book, datafile);
- return TRUE;
- }
- else
- {
- if (g_unlink (tmp_name) != 0)
- {
- switch (errno)
- {
- case ENOENT: /* tmp_name doesn't exist? Assume "RO" error */
- case EACCES:
- case EPERM:
- case ENOSYS:
- case EROFS:
- be_err = ERR_BACKEND_READONLY;
- break;
- default:
- be_err = ERR_BACKEND_MISC;
- break;
- }
- qof_backend_set_error (qof_be, be_err);
- PWARN ("unable to unlink temp_filename %s: %s",
- tmp_name ? tmp_name : "(null)",
- g_strerror (errno) ? g_strerror (errno) : "");
- /* already in an error just flow on through */
- }
- else
- {
- /* Use a generic write error code */
- qof_backend_set_error (qof_be, ERR_FILEIO_WRITE_ERROR);
- qof_backend_set_message (qof_be, "Unable to write to temp file %s",
- tmp_name ? tmp_name : "NULL");
- }
- g_free (tmp_name);
- LEAVE ("");
- return FALSE;
- }
- LEAVE ("");
- return TRUE;
-}
-
-/* ================================================================= */
-
-/*
- * Clean up any lock files from prior crashes, and clean up old
- * backup and log files.
- */
-
-static void
-gnc_xml_be_remove_old_files (XmlBackend* xml_be)
-{
- const gchar* dent;
- GDir* dir;
- struct stat lockstatbuf, statbuf;
- time64 now;
-
- if (g_stat (xml_be->m_lockfile, &lockstatbuf) != 0)
- return;
-
- dir = g_dir_open (xml_be->m_dirname, 0, NULL);
- if (!dir)
- return;
-
- now = gnc_time (NULL);
- while ((dent = g_dir_read_name (dir)) != NULL)
- {
- gchar* name;
-
- /* Ensure we only evaluate GnuCash related files. */
- if (! (g_str_has_suffix (dent, ".LNK") ||
- g_str_has_suffix (dent, ".xac") /* old data file extension */ ||
- g_str_has_suffix (dent, GNC_DATAFILE_EXT) ||
- g_str_has_suffix (dent, GNC_LOGFILE_EXT)))
- continue;
-
- name = g_build_filename (xml_be->m_dirname, dent, (gchar*)NULL);
-
- /* Only evaluate files associated with the current data file. */
- if (!g_str_has_prefix (name, xml_be->m_fullpath))
- {
- g_free (name);
- continue;
- }
-
- /* Never remove the current data file itself */
- if (g_strcmp0 (name, xml_be->m_fullpath) == 0)
- {
- g_free (name);
- continue;
- }
-
- /* Test if the current file is a lock file */
- if (g_str_has_suffix (name, ".LNK"))
- {
- /* Is a lock file. Skip the active lock file */
- if ((g_strcmp0 (name, xml_be->m_linkfile) != 0) &&
- /* Only delete lock files older than the active one */
- (g_stat (name, &statbuf) == 0) &&
- (statbuf.st_mtime < lockstatbuf.st_mtime))
- {
- PINFO ("remove stale lock file: %s", name);
- g_unlink (name);
- }
-
- g_free (name);
- continue;
- }
-
- /* At this point we're sure the file's name is in one of these forms:
- * <fullpath/to/datafile><anything>.gnucash
- * <fullpath/to/datafile><anything>.xac
- * <fullpath/to/datafile><anything>.log
- *
- * To be a file generated by GnuCash, the <anything> part should consist
- * of 1 dot followed by 14 digits (0 to 9). Let's test this with a
- * regular expression.
- */
- {
- /* Find the start of the date stamp. This takes some pointer
- * juggling, but considering the above tests, this should always
- * be safe */
- regex_t pattern;
- gchar* stamp_start = name + strlen (xml_be->m_fullpath);
- gchar* expression = g_strdup_printf ("^\\.[[:digit:]]{14}(\\%s|\\%s|\\.xac)$",
- GNC_DATAFILE_EXT, GNC_LOGFILE_EXT);
- gboolean got_date_stamp = FALSE;
-
- if (regcomp (&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
- PWARN ("Cannot compile regex for date stamp");
- else if (regexec (&pattern, stamp_start, 0, NULL, 0) == 0)
- got_date_stamp = TRUE;
-
- regfree (&pattern);
- g_free (expression);
-
- if (!got_date_stamp) /* Not a gnucash created file after all... */
- {
- g_free (name);
- continue;
- }
- }
-
- /* The file is a backup or log file. Check the user's retention preference
- * to determine if we should keep it or not
- */
- if (gnc_prefs_get_file_retention_policy () == XML_RETAIN_NONE)
- {
- PINFO ("remove stale file: %s - reason: preference XML_RETAIN_NONE", name);
- g_unlink (name);
- }
- else if ((gnc_prefs_get_file_retention_policy () == XML_RETAIN_DAYS) &&
- (gnc_prefs_get_file_retention_days () > 0))
- {
- int days;
-
- /* Is the backup file old enough to delete */
- if (g_stat (name, &statbuf) != 0)
- {
- g_free (name);
- continue;
- }
- days = (int) (difftime (now, statbuf.st_mtime) / 86400);
-
- PINFO ("file retention = %d days", gnc_prefs_get_file_retention_days ());
- if (days >= gnc_prefs_get_file_retention_days ())
- {
- PINFO ("remove stale file: %s - reason: more than %d days old", name, days);
- g_unlink (name);
- }
- }
- g_free (name);
- }
- g_dir_close (dir);
-}
-
static void
xml_sync_all (QofBackend* qof_be, QofBook* book)
{
- XmlBackend* xml_be = (XmlBackend*) qof_be;
- ENTER ("book=%p, xml_be->m_book=%p", book, xml_be->m_book);
+ GncXmlBackend* xml_be = reinterpret_cast<decltype(xml_be)>(qof_be);
+ xml_be->sync(book);
+ ENTER ("book=%p, xml_be->m_book=%p", book, xml_be->get_book());
- /* We make an important assumption here, that we might want to change
- * in the future: when the user says 'save', we really save the one,
- * the only, the current open book, and nothing else. In any case the plans
- * for multiple books have been removed in the meantime and there is just one
- * book, no more.
- */
- if (NULL == xml_be->m_book) xml_be->m_book = book;
- if (book != xml_be->m_book) return;
-
- if (qof_book_is_readonly (xml_be->m_book))
- {
- /* Are we read-only? Don't continue in this case. */
- qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
- return;
- }
-
- gnc_xml_be_write_to_file (xml_be, book, xml_be->m_fullpath, TRUE);
- gnc_xml_be_remove_old_files (xml_be);
LEAVE ("book=%p", book);
}
-/* ================================================================= */
-/* Routines to deal with the creation of multiple books.
- * The core design assumption here is that the book
- * begin-edit/commit-edit routines are used solely to write out
- * closed accounting periods to files. They're not currently
- * designed to do anything other than this. (Although they could be).
- */
-
-static char*
-build_period_filepath (XmlBackend* xml_be, QofBook* book)
-{
- int len;
- char* str, *p, *q;
-
- len = strlen (xml_be->m_fullpath) + GUID_ENCODING_LENGTH + 14;
- str = g_new (char, len);
- strcpy (str, xml_be->m_fullpath);
-
- /* XXX it would be nice for the user if we made the book
- * closing date and/or title part of the file-name. */
- p = strrchr (str, G_DIR_SEPARATOR);
- p++;
- p = stpcpy (p, "book-");
- p = guid_to_string_buff (qof_book_get_guid (book), p);
- p = stpcpy (p, "-");
- q = strrchr (xml_be->m_fullpath, G_DIR_SEPARATOR);
- q++;
- p = stpcpy (p, q);
- p = stpcpy (p, ".gml");
-
- return str;
-}
static void
xml_begin_edit (QofBackend* qof_be, QofInstance* inst)
{
- if (0) build_period_filepath (0, 0);
-#if BORKEN_FOR_NOW
- XmlBackend* xml_be = (XmlBackend*) qof_be;
- QofBook* book = gp;
- const char* filepath;
-
- QofIdTypeConst typ = QOF_INSTANCE (inst)->e_type;
- if (strcmp (GNC_ID_PERIOD, typ)) return;
- filepath = build_period_filepath (xml_be, book);
- PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
-
- if (NULL == xml_be->m_primary_book)
- {
- PERR ("You should have saved the data "
- "at least once before closing the books!\n");
- }
- /* XXX To be anal about it, we should really be checking to see
- * if there already is a file with this book GncGUID, and disallowing
- * further progress. This is because we are not allowed to
- * modify books that are closed (They should be treated as
- * 'read-only').
- */
-#endif
+ GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
+ xml_be->begin(inst);
}
static void
xml_rollback_edit (QofBackend* qof_be, QofInstance* inst)
{
-#if BORKEN_FOR_NOW
- QofBook* book = gp;
- if (strcmp (GNC_ID_PERIOD, typ)) return;
- PINFO ("book=%p", book);
-#endif
+ GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
+ xml_be->rollback(inst);
}
/* ---------------------------------------------------------------------- */
@@ -1130,94 +240,18 @@ static void
gnc_xml_be_load_from_file (QofBackend* qof_be, QofBook* book,
QofBackendLoadType loadType)
{
- QofBackendError error;
- gboolean rc;
- XmlBackend* xml_be = (XmlBackend*) qof_be;
-
- if (loadType != LOAD_TYPE_INITIAL_LOAD) return;
-
- error = ERR_BACKEND_NO_ERR;
- xml_be->m_book = book;
-
- switch (gnc_xml_be_determine_file_type (xml_be->m_fullpath))
- {
- case GNC_BOOK_XML2_FILE:
- rc = qof_session_load_from_xml_file_v2 (xml_be, book, GNC_BOOK_XML2_FILE);
- if (FALSE == rc)
- {
- PWARN ("Syntax error in Xml File %s", xml_be->m_fullpath);
- error = ERR_FILEIO_PARSE_ERROR;
- }
- break;
-
- case GNC_BOOK_XML2_FILE_NO_ENCODING:
- error = ERR_FILEIO_NO_ENCODING;
- PWARN ("No character encoding in Xml File %s", xml_be->m_fullpath);
- break;
- case GNC_BOOK_XML1_FILE:
- rc = qof_session_load_from_xml_file (book, xml_be->m_fullpath);
- if (FALSE == rc)
- {
- PWARN ("Syntax error in Xml File %s", xml_be->m_fullpath);
- error = ERR_FILEIO_PARSE_ERROR;
- }
- break;
- case GNC_BOOK_POST_XML2_0_0_FILE:
- error = ERR_BACKEND_TOO_NEW;
- PWARN ("Version of Xml file %s is newer than what we can read", xml_be->m_fullpath);
- break;
- default:
- /* If file type wasn't known, check errno again to give the
- user some more useful feedback for some particular error
- conditions. */
- switch (errno)
- {
- case EACCES: /* No read permission */
- PWARN ("No read permission to file");
- error = ERR_FILEIO_FILE_EACCES;
- break;
- case EISDIR: /* File is a directory - but on this error we don't arrive here */
- PWARN ("Filename is a directory");
- error = ERR_FILEIO_FILE_NOT_FOUND;
- break;
- default:
- PWARN ("File not any known type");
- error = ERR_FILEIO_UNKNOWN_FILE_TYPE;
- break;
- }
- break;
- }
-
- if (error != ERR_BACKEND_NO_ERR)
- {
- qof_backend_set_error (qof_be, error);
- }
-
- /* We just got done loading, it can't possibly be dirty !! */
- qof_book_mark_session_saved (book);
+ GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
+ xml_be->load(book, loadType);
}
/* ---------------------------------------------------------------------- */
-static gboolean
-save_may_clobber_data (XmlBackend *xml_be)
-{
- struct stat statbuf;
- if (!xml_be->m_fullpath) return FALSE;
-
- /* FIXME: Make sure this doesn't need more sophisticated semantics
- * in the face of special file, devices, pipes, symlinks, etc. */
- if (g_stat (xml_be->m_fullpath, &statbuf) == 0) return TRUE;
- return FALSE;
-}
static void
gnc_xml_be_write_accounts_to_file (QofBackend* qof_be, QofBook* book)
{
- const gchar* datafile;
-
- datafile = ((XmlBackend*)qof_be)->m_fullpath;
+ auto datafile = ((GncXmlBackend*)qof_be)->get_filename();
gnc_book_write_accounts_to_xml_file_v2 (qof_be, book, datafile);
}
@@ -1226,13 +260,9 @@ gnc_xml_be_write_accounts_to_file (QofBackend* qof_be, QofBook* book)
QofBackend*
QofXmlBackendProvider::create_backend(void)
{
- XmlBackend* xml_be;
- QofBackend* qof_be;
-
- xml_be = g_new0 (XmlBackend, 1);
- qof_be = (QofBackend*) xml_be;
- qof_backend_init (qof_be);
+ auto xml_be = new GncXmlBackend;
+ auto qof_be = xml_be->get_qof_be();
qof_be->session_begin = xml_session_begin;
qof_be->session_end = xml_session_end;
qof_be->destroy_backend = xml_destroy_backend;
@@ -1248,14 +278,6 @@ QofXmlBackendProvider::create_backend(void)
qof_be->export_fn = gnc_xml_be_write_accounts_to_file;
- xml_be->m_dirname = NULL;
- xml_be->m_fullpath = NULL;
- xml_be->m_lockfile = NULL;
- xml_be->m_linkfile = NULL;
- xml_be->m_lockfd = -1;
-
- xml_be->m_book = NULL;
-
return qof_be;
}
diff --git a/src/backend/xml/gnc-backend-xml.h b/src/backend/xml/gnc-backend-xml.h
index c5fa189..f82bc25 100644
--- a/src/backend/xml/gnc-backend-xml.h
+++ b/src/backend/xml/gnc-backend-xml.h
@@ -71,19 +71,5 @@ void qof_backend_module_init (void);
#ifdef __cplusplus
}
-class XmlBackend
-{
-public:
- QofBackend be;
-
- char* m_dirname;
- char* m_fullpath; /* Fully qualified path to book */
- char* m_lockfile;
- char* m_linkfile;
- int m_lockfd;
-
- QofBook* m_book; /* The primary, main open book */
-};
-
#endif
#endif /* GNC_BACKEND_XML_H_ */
diff --git a/src/backend/xml/gnc-xml-backend.cpp b/src/backend/xml/gnc-xml-backend.cpp
new file mode 100644
index 0000000..213208b
--- /dev/null
+++ b/src/backend/xml/gnc-xml-backend.cpp
@@ -0,0 +1,892 @@
+/********************************************************************
+ * gnc-xml-backend.cpp: Implement XML file backend. *
+ * Copyright 2016 John Ralls <jralls at ceridwen.us> *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY 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, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+\********************************************************************/
+
+extern "C"
+{
+#include <config.h>
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <regex.h>
+
+#include <gnc-engine.h> //for GNC_MOD_BACKEND
+#include <gnc-uri-utils.h>
+#include <TransLog.h>
+#include <gnc-prefs.h>
+
+}
+
+#include <sstream>
+
+#include "gnc-xml-backend.hpp"
+#include "gnc-backend-xml.h"
+#include "io-gncxml-v2.h"
+#include "io-gncxml.h"
+
+#define XML_URI_PREFIX "xml://"
+#define FILE_URI_PREFIX "file://"
+static QofLogModule log_module = GNC_MOD_BACKEND;
+
+GncXmlBackend::GncXmlBackend()
+{
+ memset(&qof_be, 0, sizeof(qof_be));
+ qof_backend_init(&qof_be);
+}
+
+GncXmlBackend::~GncXmlBackend()
+{
+ /* Stop transaction logging */
+ xaccLogSetBaseName (NULL);
+ qof_backend_destroy (&qof_be);
+}
+
+bool
+check_path (const char* fullpath, QofBackend* qof_be, bool create)
+{
+ struct stat statbuf;
+ char* dirname = g_path_get_dirname (fullpath);
+ /* Again check whether the directory can be accessed */
+ auto rc = g_stat (dirname, &statbuf);
+ if (rc != 0
+#if COMPILER(MSVC)
+ || (statbuf.st_mode & _S_IFDIR) == 0
+#else
+ || !S_ISDIR (statbuf.st_mode)
+#endif
+ )
+ {
+ /* Error on stat or if it isn't a directory means we
+ cannot find this filename */
+ qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
+ qof_backend_set_message (qof_be, "Couldn't find directory for %s",
+ fullpath);
+ PWARN ("Couldn't find directory for %s", fullpath);
+ g_free(dirname);
+ return false;
+ }
+
+ /* Now check whether we can g_stat the file itself */
+ rc = g_stat (fullpath, &statbuf);
+ if ((rc != 0) && (!create))
+ {
+ /* Error on stat means the file doesn't exist */
+ qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
+ qof_backend_set_message (qof_be, "Couldn't find %s", fullpath);
+ PWARN ("Couldn't find %s", fullpath);
+ g_free(dirname);
+ return false;
+ }
+ if (rc == 0
+#if COMPILER(MSVC)
+ && (statbuf.st_mode & _S_IFDIR) != 0
+#else
+ && S_ISDIR (statbuf.st_mode)
+#endif
+ )
+ {
+ qof_backend_set_error (qof_be, ERR_FILEIO_UNKNOWN_FILE_TYPE);
+ qof_backend_set_message (qof_be, "Path %s is a directory", fullpath);
+ PWARN ("Path %s is a directory", fullpath);
+ g_free(dirname);
+ return false;
+ }
+ return true;
+}
+
+void
+GncXmlBackend::session_begin(QofSession* session, const char* book_id,
+ bool ignore_lock, bool create, bool force)
+{
+ /* Make sure the directory is there */
+ m_fullpath = gnc_uri_get_path (book_id);
+
+ if (m_fullpath.empty())
+ {
+ qof_backend_set_error (&qof_be, ERR_FILEIO_FILE_NOT_FOUND);
+ qof_backend_set_message (&qof_be, "No path specified");
+ return;
+ }
+ if (create && !force && save_may_clobber_data())
+ {
+ qof_backend_set_error (&qof_be, ERR_BACKEND_STORE_EXISTS);
+ PWARN ("Might clobber, no force");
+ return;
+ }
+
+ if (!check_path(m_fullpath.c_str(), &qof_be, create))
+ return;
+ qof_be.fullpath = const_cast<char*>(m_fullpath.c_str());
+ m_dirname = g_path_get_dirname (m_fullpath.c_str());
+
+
+
+ /* ---------------------------------------------------- */
+ /* We should now have a fully resolved path name.
+ * Let's start logging */
+ xaccLogSetBaseName (m_fullpath.c_str());
+ PINFO ("logpath=%s", m_fullpath.empty() ? "(null)" : m_fullpath.c_str());
+
+ /* And let's see if we can get a lock on it. */
+ m_lockfile = m_fullpath + ".LCK";
+
+ if (!ignore_lock && !get_file_lock())
+ {
+ // We should not ignore the lock, but couldn't get it. The
+ // be_get_file_lock() already set the appropriate backend_error in this
+ // case, so we just return here.
+ m_lockfile.clear();
+
+ if (force)
+ {
+ QofBackendError berror = qof_backend_get_error (&qof_be);
+ if (berror == ERR_BACKEND_LOCKED || berror == ERR_BACKEND_READONLY)
+ {
+ // Even though we couldn't get the lock, we were told to force
+ // the opening. This is ok because the FORCE argument is
+ // changed only if the caller wants a read-only book.
+ }
+ else
+ {
+ // Unknown error. Push it again on the error stack.
+ qof_backend_set_error (&qof_be, berror);
+ }
+ }
+ }
+
+}
+
+void
+GncXmlBackend::session_end()
+{
+ if (m_book && qof_book_is_readonly (m_book))
+ {
+ qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ return;
+ }
+
+ if (!m_linkfile.empty())
+ g_unlink (m_linkfile.c_str());
+
+ if (m_lockfd > 0)
+ close (m_lockfd);
+
+ if (!m_lockfile.empty())
+ {
+ int rv;
+#ifdef G_OS_WIN32
+ /* On windows, we need to allow write-access before
+ g_unlink() can succeed */
+ rv = g_chmod (m_lockfile.c_str(), S_IWRITE | S_IREAD);
+#endif
+ rv = g_unlink (m_lockfile.c_str());
+ if (rv)
+ {
+ PWARN ("Error on g_unlink(%s): %d: %s", m_lockfile.c_str(),
+ errno, g_strerror (errno) ? g_strerror (errno) : "");
+ }
+ }
+
+ m_dirname.clear();
+ m_fullpath.clear();
+ m_lockfile.clear();
+ m_linkfile.clear();
+}
+
+static QofBookFileType
+determine_file_type (const std::string& path)
+{
+ gboolean with_encoding;
+ QofBookFileType v2type;
+
+ v2type = gnc_is_xml_data_file_v2 (path.c_str(), &with_encoding);
+ if (v2type == GNC_BOOK_XML2_FILE)
+ {
+ if (with_encoding)
+ {
+ return GNC_BOOK_XML2_FILE;
+ }
+ else
+ {
+ return GNC_BOOK_XML2_FILE_NO_ENCODING;
+ }
+ }
+ else if (v2type == GNC_BOOK_POST_XML2_0_0_FILE)
+ {
+ return GNC_BOOK_POST_XML2_0_0_FILE;
+ }
+ else if (v2type == GNC_BOOK_XML1_FILE)
+ {
+ return GNC_BOOK_XML1_FILE;
+ }
+ return GNC_BOOK_NOT_OURS;
+}
+
+void
+GncXmlBackend::load(QofBook* book, QofBackendLoadType loadType)
+{
+
+ QofBackendError error;
+
+ if (loadType != LOAD_TYPE_INITIAL_LOAD) return;
+
+ error = ERR_BACKEND_NO_ERR;
+ m_book = book;
+
+ int rc;
+ switch (determine_file_type (m_fullpath))
+ {
+ case GNC_BOOK_XML2_FILE:
+ rc = qof_session_load_from_xml_file_v2 (this, book,
+ GNC_BOOK_XML2_FILE);
+ if (rc == FALSE)
+ {
+ PWARN ("Syntax error in Xml File %s", m_fullpath.c_str());
+ error = ERR_FILEIO_PARSE_ERROR;
+ }
+ break;
+
+ case GNC_BOOK_XML2_FILE_NO_ENCODING:
+ error = ERR_FILEIO_NO_ENCODING;
+ PWARN ("No character encoding in Xml File %s", m_fullpath.c_str());
+ break;
+ case GNC_BOOK_XML1_FILE:
+ rc = qof_session_load_from_xml_file (book, m_fullpath.c_str());
+ if (rc == FALSE)
+ {
+ PWARN ("Syntax error in Xml File %s", m_fullpath.c_str());
+ error = ERR_FILEIO_PARSE_ERROR;
+ }
+ break;
+ case GNC_BOOK_POST_XML2_0_0_FILE:
+ error = ERR_BACKEND_TOO_NEW;
+ PWARN ("Version of Xml file %s is newer than what we can read",
+ m_fullpath.c_str());
+ break;
+ default:
+ /* If file type wasn't known, check errno again to give the
+ user some more useful feedback for some particular error
+ conditions. */
+ switch (errno)
+ {
+ case EACCES: /* No read permission */
+ PWARN ("No read permission to file");
+ error = ERR_FILEIO_FILE_EACCES;
+ break;
+ case EISDIR: /* File is a directory - but on this error we don't arrive here */
+ PWARN ("Filename is a directory");
+ error = ERR_FILEIO_FILE_NOT_FOUND;
+ break;
+ default:
+ PWARN ("File not any known type");
+ error = ERR_FILEIO_UNKNOWN_FILE_TYPE;
+ break;
+ }
+ break;
+ }
+
+ if (error != ERR_BACKEND_NO_ERR)
+ {
+ qof_backend_set_error (&qof_be, error);
+ }
+
+ /* We just got done loading, it can't possibly be dirty !! */
+ qof_book_mark_session_saved (book);
+}
+
+void
+GncXmlBackend::sync(QofBook* book)
+{
+ /* We make an important assumption here, that we might want to change
+ * in the future: when the user says 'save', we really save the one,
+ * the only, the current open book, and nothing else. In any case the plans
+ * for multiple books have been removed in the meantime and there is just one
+ * book, no more.
+ */
+ if (m_book == nullptr) m_book = book;
+ if (book != m_book) return;
+
+ if (qof_book_is_readonly (m_book))
+ {
+ /* Are we read-only? Don't continue in this case. */
+ qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ return;
+ }
+
+ write_to_file (true);
+ remove_old_files();
+}
+
+bool
+GncXmlBackend::save_may_clobber_data()
+{
+ if (m_fullpath.empty())
+ return false;
+ struct stat statbuf;
+ auto rc = g_stat (m_fullpath.c_str(), &statbuf);
+ return rc != 0;
+
+}
+
+bool
+GncXmlBackend::write_to_file (bool make_backup)
+{
+ QofBackendError be_err;
+
+ ENTER (" book=%p file=%s", m_book, m_fullpath.c_str());
+
+ if (m_book && qof_book_is_readonly (m_book))
+ {
+ /* Are we read-only? Don't continue in this case. */
+ qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ LEAVE ("");
+ return FALSE;
+ }
+
+ /* If the book is 'clean', recently saved, then don't save again. */
+ /* XXX this is currently broken due to faulty 'Save As' logic. */
+ /* if (FALSE == qof_book_session_not_saved (book)) return FALSE; */
+
+
+ auto tmp_name = g_new (char, strlen (m_fullpath.c_str()) + 12);
+ strcpy (tmp_name, m_fullpath.c_str());
+ strcat (tmp_name, ".tmp-XXXXXX");
+
+ if (!mktemp (tmp_name))
+ {
+ qof_backend_set_error (&qof_be, ERR_BACKEND_MISC);
+ qof_backend_set_message (&qof_be, "Failed to make temp file");
+ LEAVE ("");
+ return FALSE;
+ }
+
+ if (make_backup)
+ {
+ if (!backup_file ())
+ {
+ LEAVE ("");
+ return FALSE;
+ }
+ }
+
+ if (gnc_book_write_to_xml_file_v2 (m_book, tmp_name,
+ gnc_prefs_get_file_save_compressed ()))
+ {
+ /* Record the file's permissions before g_unlinking it */
+ struct stat statbuf;
+ auto rc = g_stat (m_fullpath.c_str(), &statbuf);
+ if (rc == 0)
+ {
+ /* We must never chmod the file /dev/null */
+ g_assert (g_strcmp0 (tmp_name, "/dev/null") != 0);
+
+ /* Use the permissions from the original data file */
+ if (g_chmod (tmp_name, statbuf.st_mode) != 0)
+ {
+ /* qof_backend_set_error(&qof_be, ERR_BACKEND_PERM); */
+ /* qof_backend_set_message(&qof_be, "Failed to chmod filename %s", tmp_name ); */
+ /* Even if the chmod did fail, the save
+ nevertheless completed successfully. It is
+ therefore wrong to signal the ERR_BACKEND_PERM
+ error here which implies that the saving itself
+ failed. Instead, we simply ignore this. */
+ PWARN ("unable to chmod filename %s: %s",
+ tmp_name ? tmp_name : "(null)",
+ g_strerror (errno) ? g_strerror (errno) : "");
+#if VFAT_DOESNT_SUCK /* chmod always fails on vfat/samba fs */
+ /* g_free(tmp_name); */
+ /* return FALSE; */
+#endif
+ }
+#ifdef HAVE_CHOWN
+ /* Don't try to change the owner. Only root can do
+ that. */
+ if (chown (tmp_name, -1, statbuf.st_gid) != 0)
+ {
+ /* qof_backend_set_error(&qof_be, ERR_BACKEND_PERM); */
+ /* qof_backend_set_message(&qof_be, "Failed to chown filename %s", tmp_name ); */
+ /* A failed chown doesn't mean that the saving itself
+ failed. So don't abort with an error here! */
+ PWARN ("unable to chown filename %s: %s",
+ tmp_name ? tmp_name : "(null)",
+ strerror (errno) ? strerror (errno) : "");
+#if VFAT_DOESNT_SUCK /* chown always fails on vfat fs */
+ /* g_free(tmp_name);
+ return FALSE; */
+#endif
+ }
+#endif
+ }
+ if (g_unlink (m_fullpath.c_str()) != 0 && errno != ENOENT)
+ {
+ qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
+ PWARN ("unable to unlink filename %s: %s",
+ m_fullpath.empty() ? "(null)" : m_fullpath.c_str(),
+ g_strerror (errno) ? g_strerror (errno) : "");
+ g_free (tmp_name);
+ LEAVE ("");
+ return FALSE;
+ }
+ if (!link_or_make_backup (tmp_name, m_fullpath))
+ {
+ qof_backend_set_error (&qof_be, ERR_FILEIO_BACKUP_ERROR);
+ qof_backend_set_message (&qof_be, "Failed to make backup file %s",
+ m_fullpath.empty() ? "NULL" : m_fullpath.c_str());
+ g_free (tmp_name);
+ LEAVE ("");
+ return FALSE;
+ }
+ if (g_unlink (tmp_name) != 0)
+ {
+ qof_backend_set_error (&qof_be, ERR_BACKEND_PERM);
+ PWARN ("unable to unlink temp filename %s: %s",
+ tmp_name ? tmp_name : "(null)",
+ g_strerror (errno) ? g_strerror (errno) : "");
+ g_free (tmp_name);
+ LEAVE ("");
+ return FALSE;
+ }
+ g_free (tmp_name);
+
+ /* Since we successfully saved the book,
+ * we should mark it clean. */
+ qof_book_mark_session_saved (m_book);
+ LEAVE (" successful save of book=%p to file=%s", m_book,
+ m_fullpath.c_str());
+ return TRUE;
+ }
+ else
+ {
+ if (g_unlink (tmp_name) != 0)
+ {
+ switch (errno)
+ {
+ case ENOENT: /* tmp_name doesn't exist? Assume "RO" error */
+ case EACCES:
+ case EPERM:
+ case ENOSYS:
+ case EROFS:
+ be_err = ERR_BACKEND_READONLY;
+ break;
+ default:
+ be_err = ERR_BACKEND_MISC;
+ break;
+ }
+ qof_backend_set_error (&qof_be, be_err);
+ PWARN ("unable to unlink temp_filename %s: %s",
+ tmp_name ? tmp_name : "(null)",
+ g_strerror (errno) ? g_strerror (errno) : "");
+ /* already in an error just flow on through */
+ }
+ else
+ {
+ /* Use a generic write error code */
+ qof_backend_set_error (&qof_be, ERR_FILEIO_WRITE_ERROR);
+ qof_backend_set_message (&qof_be, "Unable to write to temp file %s",
+ tmp_name ? tmp_name : "NULL");
+ }
+ g_free (tmp_name);
+ LEAVE ("");
+ return FALSE;
+ }
+ LEAVE ("");
+ return TRUE;
+}
+
+static bool
+copy_file (const std::string& orig, const std::string& bkup)
+{
+ constexpr size_t buf_size = 1024;
+ char buf[buf_size];
+ int flags = 0;
+ ssize_t count_write;
+ ssize_t count_read;
+
+
+#ifdef G_OS_WIN32
+ flags = O_BINARY;
+#endif
+
+ auto orig_fd = g_open (orig.c_str(), O_RDONLY | flags, 0);
+ if (orig_fd == -1)
+ {
+ return false;
+ }
+ auto bkup_fd = g_open (bkup.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | flags, 0600);
+ if (bkup_fd == -1)
+ {
+ close (orig_fd);
+ return FALSE;
+ }
+
+ do
+ {
+ auto count_read = read (orig_fd, buf, buf_size);
+ if (count_read == -1 && errno != EINTR)
+ {
+ close (orig_fd);
+ close (bkup_fd);
+ return FALSE;
+ }
+
+ if (count_read > 0)
+ {
+ count_write = write (bkup_fd, buf, count_read);
+ if (count_write == -1)
+ {
+ close (orig_fd);
+ close (bkup_fd);
+ return FALSE;
+ }
+ }
+ }
+ while (count_read > 0);
+
+ close (orig_fd);
+ close (bkup_fd);
+
+ return TRUE;
+}
+
+bool
+GncXmlBackend::link_or_make_backup (const std::string& orig,
+ const std::string& bkup)
+{
+ gboolean copy_success = FALSE;
+ int err_ret =
+#ifdef HAVE_LINK
+ link (orig.c_str(), bkup.c_str())
+#else
+ - 1
+#endif
+ ;
+ if (err_ret != 0)
+ {
+#ifdef HAVE_LINK
+ if (errno == EPERM || errno == ENOSYS
+# ifdef EOPNOTSUPP
+ || errno == EOPNOTSUPP
+# endif
+# ifdef ENOTSUP
+ || errno == ENOTSUP
+# endif
+# ifdef ENOSYS
+ || errno == ENOSYS
+# endif
+ )
+#endif
+ {
+ copy_success = copy_file (orig.c_str(), bkup);
+ }
+
+ if (!copy_success)
+ {
+ qof_backend_set_error (&qof_be, ERR_FILEIO_BACKUP_ERROR);
+ PWARN ("unable to make file backup from %s to %s: %s",
+ orig.c_str(), bkup.c_str(), g_strerror (errno) ? g_strerror (errno) : "");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+GncXmlBackend::get_file_lock ()
+{
+ struct stat statbuf;
+#ifndef G_OS_WIN32
+ char* pathbuf = NULL, *tmpbuf = NULL;
+ size_t pathbuf_size = 0;
+#endif
+ QofBackendError be_err;
+
+ auto rc = g_stat (m_lockfile.c_str(), &statbuf);
+ if (!rc)
+ {
+ /* oops .. file is locked by another user .. */
+ qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ return false;
+ }
+
+ m_lockfd = g_open (m_lockfile.c_str(), O_RDWR | O_CREAT | O_EXCL ,
+ S_IRUSR | S_IWUSR);
+ if (m_lockfd < 0)
+ {
+ /* oops .. we can't create the lockfile .. */
+ switch (errno)
+ {
+ case EACCES:
+ case EROFS:
+ case ENOSPC:
+ PWARN ("Unable to create the lockfile %s; may not have write priv",
+ m_lockfile.c_str());
+ be_err = ERR_BACKEND_READONLY;
+ break;
+ default:
+ be_err = ERR_BACKEND_LOCKED;
+ break;
+ }
+ qof_backend_set_error (&qof_be, be_err);
+ return false;
+ }
+
+ /* OK, now work around some NFS atomic lock race condition
+ * mumbo-jumbo. We do this by linking a unique file, and
+ * then examining the link count. At least that's what the
+ * NFS programmers guide suggests.
+ * Note: the "unique filename" must be unique for the
+ * triplet filename-host-process, otherwise accidental
+ * aliases can occur.
+ */
+
+ /* apparently, even this code may not work for some NFS
+ * implementations. In the long run, I am told that
+ * ftp.debian.org
+ * /pub/debian/dists/unstable/main/source/libs/liblockfile_0.1-6.tar.gz
+ * provides a better long-term solution.
+ */
+
+#ifndef G_OS_WIN32
+ auto path = m_lockfile.find_last_of('.');
+ std::stringstream linkfile;
+ if (path != std::string::npos)
+ linkfile << m_lockfile.substr(0, path);
+ else
+ linkfile << m_lockfile;
+ linkfile << "." << gethostid() << "." << getpid() << ".LNK";
+ rc = link (m_lockfile.c_str(), linkfile.str().c_str());
+ if (rc)
+ {
+ /* If hard links aren't supported, just allow the lock. */
+ if (errno == EPERM || errno == ENOSYS
+# ifdef EOPNOTSUPP
+ || errno == EOPNOTSUPP
+# endif
+# ifdef ENOTSUP
+ || errno == ENOTSUP
+# endif
+ )
+ {
+ return true;
+ }
+
+ /* Otherwise, something else is wrong. */
+ qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ g_unlink (linkfile.str().c_str());
+ close (m_lockfd);
+ g_unlink (m_lockfile.c_str());
+ return false;
+ }
+
+ rc = g_stat (m_lockfile.c_str(), &statbuf);
+ if (rc)
+ {
+ /* oops .. stat failed! This can't happen! */
+ qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ qof_backend_set_message (&qof_be, "Failed to stat lockfile %s",
+ m_lockfile.c_str());
+ g_unlink (linkfile.str().c_str());
+ close (m_lockfd);
+ g_unlink (m_lockfile.c_str());
+ return false;
+ }
+
+ if (statbuf.st_nlink != 2)
+ {
+ qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
+ g_unlink (linkfile.str().c_str());
+ close (m_lockfd);
+ g_unlink (m_lockfile.c_str());
+ return false;
+ }
+
+ m_linkfile = linkfile.str();
+ return true;
+
+#else /* ifndef G_OS_WIN32 */
+ /* On windows, there is no NFS and the open(,O_CREAT | O_EXCL)
+ is sufficient for locking. */
+ return true;
+#endif /* ifndef G_OS_WIN32 */
+}
+
+bool
+GncXmlBackend::backup_file()
+{
+ struct stat statbuf;
+
+ auto datafile = m_fullpath.c_str();
+
+ auto rc = g_stat (datafile, &statbuf);
+ if (rc)
+ return (errno == ENOENT);
+
+ if (determine_file_type (m_fullpath) == GNC_BOOK_BIN_FILE)
+ {
+ /* make a more permanent safer backup */
+ auto bin_bkup = m_fullpath + "-binfmt.bkup";
+ auto bkup_ret = link_or_make_backup (m_fullpath, bin_bkup);
+ if (!bkup_ret)
+ {
+ return false;
+ }
+ }
+
+ auto timestamp = gnc_date_timestamp ();
+ auto backup = m_fullpath + "." + timestamp + GNC_DATAFILE_EXT;
+ g_free (timestamp);
+
+ return link_or_make_backup (datafile, backup);
+}
+
+/*
+ * Clean up any lock files from prior crashes, and clean up old
+ * backup and log files.
+ */
+
+void
+GncXmlBackend::remove_old_files ()
+{
+ struct stat lockstatbuf, statbuf;
+
+ if (g_stat (m_lockfile.c_str(), &lockstatbuf) != 0)
+ return;
+
+ auto dir = g_dir_open (m_dirname.c_str(), 0, NULL);
+ if (!dir)
+ return;
+
+ auto now = gnc_time (NULL);
+ const char* dent;
+ while ((dent = g_dir_read_name (dir)) != NULL)
+ {
+ gchar* name;
+
+ /* Ensure we only evaluate GnuCash related files. */
+ if (! (g_str_has_suffix (dent, ".LNK") ||
+ g_str_has_suffix (dent, ".xac") /* old data file extension */ ||
+ g_str_has_suffix (dent, GNC_DATAFILE_EXT) ||
+ g_str_has_suffix (dent, GNC_LOGFILE_EXT)))
+ continue;
+
+ name = g_build_filename (m_dirname.c_str(), dent, (gchar*)NULL);
+
+ /* Only evaluate files associated with the current data file. */
+ if (!g_str_has_prefix (name, m_fullpath.c_str()))
+ {
+ g_free (name);
+ continue;
+ }
+
+ /* Never remove the current data file itself */
+ if (g_strcmp0 (name, m_fullpath.c_str()) == 0)
+ {
+ g_free (name);
+ continue;
+ }
+
+ /* Test if the current file is a lock file */
+ if (g_str_has_suffix (name, ".LNK"))
+ {
+ /* Is a lock file. Skip the active lock file */
+ if ((g_strcmp0 (name, m_linkfile.c_str()) != 0) &&
+ /* Only delete lock files older than the active one */
+ (g_stat (name, &statbuf) == 0) &&
+ (statbuf.st_mtime < lockstatbuf.st_mtime))
+ {
+ PINFO ("remove stale lock file: %s", name);
+ g_unlink (name);
+ }
+
+ g_free (name);
+ continue;
+ }
+
+ /* At this point we're sure the file's name is in one of these forms:
+ * <fullpath/to/datafile><anything>.gnucash
+ * <fullpath/to/datafile><anything>.xac
+ * <fullpath/to/datafile><anything>.log
+ *
+ * To be a file generated by GnuCash, the <anything> part should consist
+ * of 1 dot followed by 14 digits (0 to 9). Let's test this with a
+ * regular expression.
+ */
+ {
+ /* Find the start of the date stamp. This takes some pointer
+ * juggling, but considering the above tests, this should always
+ * be safe */
+ regex_t pattern;
+ gchar* stamp_start = name + strlen (m_fullpath.c_str());
+ gchar* expression = g_strdup_printf ("^\\.[[:digit:]]{14}(\\%s|\\%s|\\.xac)$",
+ GNC_DATAFILE_EXT, GNC_LOGFILE_EXT);
+ gboolean got_date_stamp = FALSE;
+
+ if (regcomp (&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
+ PWARN ("Cannot compile regex for date stamp");
+ else if (regexec (&pattern, stamp_start, 0, NULL, 0) == 0)
+ got_date_stamp = TRUE;
+
+ regfree (&pattern);
+ g_free (expression);
+
+ if (!got_date_stamp) /* Not a gnucash created file after all... */
+ {
+ g_free (name);
+ continue;
+ }
+ }
+
+ /* The file is a backup or log file. Check the user's retention preference
+ * to determine if we should keep it or not
+ */
+ if (gnc_prefs_get_file_retention_policy () == XML_RETAIN_NONE)
+ {
+ PINFO ("remove stale file: %s - reason: preference XML_RETAIN_NONE", name);
+ g_unlink (name);
+ }
+ else if ((gnc_prefs_get_file_retention_policy () == XML_RETAIN_DAYS) &&
+ (gnc_prefs_get_file_retention_days () > 0))
+ {
+ int days;
+
+ /* Is the backup file old enough to delete */
+ if (g_stat (name, &statbuf) != 0)
+ {
+ g_free (name);
+ continue;
+ }
+ days = (int) (difftime (now, statbuf.st_mtime) / 86400);
+
+ PINFO ("file retention = %d days", gnc_prefs_get_file_retention_days ());
+ if (days >= gnc_prefs_get_file_retention_days ())
+ {
+ PINFO ("remove stale file: %s - reason: more than %d days old", name, days);
+ g_unlink (name);
+ }
+ }
+ g_free (name);
+ }
+ g_dir_close (dir);
+}
diff --git a/src/backend/xml/gnc-xml-backend.hpp b/src/backend/xml/gnc-xml-backend.hpp
new file mode 100644
index 0000000..ab8c64b
--- /dev/null
+++ b/src/backend/xml/gnc-xml-backend.hpp
@@ -0,0 +1,69 @@
+/********************************************************************
+ * gnc-xml-backend.hpp: Declare XML file backend. *
+ * Copyright 2016 John Ralls <jralls at ceridwen.us> *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY 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, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+\********************************************************************/
+
+#ifndef __GNC_XML_BACKEND_HPP__
+#define __GNC_XML_BACKEND_HPP__
+
+extern "C"
+{
+#include <qof.h>
+#include <qofbackend-p.h>
+}
+
+#include <string>
+
+class GncXmlBackend
+{
+public:
+ GncXmlBackend();
+ GncXmlBackend(const GncXmlBackend&) = delete;
+ GncXmlBackend operator=(const GncXmlBackend&) = delete;
+ GncXmlBackend(const GncXmlBackend&&) = delete;
+ GncXmlBackend operator=(const GncXmlBackend&&) = delete;
+ ~GncXmlBackend();
+ void session_begin(QofSession* session, const char* book_id,
+ bool ignore_lock, bool create, bool force);
+ void session_end();
+ void load(QofBook* book, QofBackendLoadType loadType);
+ /* The XML backend isn't able to do anything with individual instances. */
+ void begin(QofInstance* inst) {}
+ void commit(QofInstance* inst) {}
+ void rollback(QofInstance* inst) {}
+ void sync(QofBook* book);
+ QofBackend* get_qof_be() { return &qof_be; }
+ const char * get_filename() { return m_fullpath.c_str(); }
+ QofBook* get_book() { return m_book; }
+
+private:
+ bool save_may_clobber_data();
+ bool get_file_lock();
+ bool link_or_make_backup(const std::string& orig, const std::string& bkup);
+ bool backup_file();
+ bool write_to_file(bool make_backup);
+ void remove_old_files();
+ void write_accounts(QofBook* book);
+ QofBackend qof_be;
+
+ std::string m_dirname;
+ std::string m_fullpath; /* Fully qualified path to book */
+ std::string m_lockfile;
+ std::string m_linkfile;
+ int m_lockfd;
+
+ QofBook* m_book; /* The primary, main open book */
+};
+#endif // __GNC_XML_BACKEND_HPP__
diff --git a/src/backend/xml/io-gncxml-v2.cpp b/src/backend/xml/io-gncxml-v2.cpp
index 2735238..d58e822 100644
--- a/src/backend/xml/io-gncxml-v2.cpp
+++ b/src/backend/xml/io-gncxml-v2.cpp
@@ -66,6 +66,7 @@ extern "C"
#endif
}
+#include "gnc-xml-backend.hpp"
#include "sixtp-parsers.h"
#include "sixtp-utils.h"
#include "gnc-xml.h"
@@ -692,7 +693,7 @@ gnc_sixtp_gdv2_new (
static gboolean
qof_session_load_from_xml_file_v2_full (
- XmlBackend* xml_be, QofBook* book,
+ GncXmlBackend* xml_be, QofBook* book,
sixtp_push_handler push_handler, gpointer push_user_data,
QofBookFileType type)
{
@@ -793,7 +794,7 @@ qof_session_load_from_xml_file_v2_full (
* https://bugzilla.gnome.org/show_bug.cgi?id=712528 for more
* info.
*/
- gchar* filename = xml_be->m_fullpath;
+ const char* filename = xml_be->get_filename();
FILE* file;
gboolean is_compressed = is_gzipped_file (filename);
file = try_gz_open (filename, "r", is_compressed, FALSE);
@@ -864,7 +865,7 @@ bail:
}
gboolean
-qof_session_load_from_xml_file_v2 (XmlBackend* xml_be, QofBook* book,
+qof_session_load_from_xml_file_v2 (GncXmlBackend* xml_be, QofBook* book,
QofBookFileType type)
{
return qof_session_load_from_xml_file_v2_full (xml_be, book, NULL, NULL, type);
@@ -2031,7 +2032,7 @@ cleanup_find_ambs:
typedef struct
{
- gchar* filename;
+ const char* filename;
GHashTable* subst;
} push_data_type;
@@ -2168,13 +2169,13 @@ cleanup_push_handler:
}
gboolean
-gnc_xml2_parse_with_subst (XmlBackend* xml_be, QofBook* book, GHashTable* subst)
+gnc_xml2_parse_with_subst (GncXmlBackend* xml_be, QofBook* book, GHashTable* subst)
{
push_data_type* push_data;
gboolean success;
push_data = g_new (push_data_type, 1);
- push_data->filename = xml_be->m_fullpath;
+ push_data->filename = xml_be->get_filename();
push_data->subst = subst;
success = qof_session_load_from_xml_file_v2_full (
diff --git a/src/backend/xml/io-gncxml-v2.h b/src/backend/xml/io-gncxml-v2.h
index 8948b05..ccfb4b3 100644
--- a/src/backend/xml/io-gncxml-v2.h
+++ b/src/backend/xml/io-gncxml-v2.h
@@ -43,7 +43,7 @@ extern "C"
#include "sixtp.h"
#include <vector>
-class XmlBackend;
+class GncXmlBackend;
/**
* Struct used to pass in a new data type for XML storage. This contains
@@ -85,7 +85,7 @@ typedef struct
} gnc_template_xaction_data;
/** read in an account group from a file */
-gboolean qof_session_load_from_xml_file_v2 (XmlBackend*, QofBook*,
+gboolean qof_session_load_from_xml_file_v2 (GncXmlBackend*, QofBook*,
QofBookFileType);
/* write all book info to a file */
@@ -155,7 +155,7 @@ gint gnc_xml2_find_ambiguous (
* @param subst hash table with keys and values of type gchar*
*/
gboolean gnc_xml2_parse_with_subst (
- XmlBackend* xml_be, QofBook* book, GHashTable* subst);
+ GncXmlBackend* xml_be, QofBook* book, GHashTable* subst);
#ifdef __cplusplus
}
typedef struct
commit 90a95204156ea52196cb9702b127ebcb9ac03c11
Author: John Ralls <jralls at ceridwen.us>
Date: Sun Nov 20 13:17:23 2016 -0800
Change struct FileBackend to class XmlBackend and rename all variables.
For consistency and clarity: all XmlBackend* are xml_be, all QofBackend*
are qof_be.
diff --git a/src/backend/xml/gnc-backend-xml.cpp b/src/backend/xml/gnc-backend-xml.cpp
index 40ab3cf..ab9e353 100644
--- a/src/backend/xml/gnc-backend-xml.cpp
+++ b/src/backend/xml/gnc-backend-xml.cpp
@@ -105,7 +105,7 @@ extern "C"
static QofLogModule log_module = GNC_MOD_BACKEND;
-static gboolean save_may_clobber_data (FileBackend *bend);
+static gboolean save_may_clobber_data (XmlBackend *xml_be);
struct QofXmlBackendProvider : public QofBackendProvider
{
@@ -124,7 +124,7 @@ struct QofXmlBackendProvider : public QofBackendProvider
/* ================================================================= */
static gboolean
-gnc_xml_be_get_file_lock (FileBackend* be)
+gnc_xml_be_get_file_lock (XmlBackend* xml_be)
{
struct stat statbuf;
#ifndef G_OS_WIN32
@@ -134,17 +134,17 @@ gnc_xml_be_get_file_lock (FileBackend* be)
int rc;
QofBackendError be_err;
- rc = g_stat (be->lockfile, &statbuf);
+ rc = g_stat (xml_be->m_lockfile, &statbuf);
if (!rc)
{
/* oops .. file is locked by another user .. */
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+ qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
return FALSE;
}
- be->lockfd = g_open (be->lockfile, O_RDWR | O_CREAT | O_EXCL ,
+ xml_be->m_lockfd = g_open (xml_be->m_lockfile, O_RDWR | O_CREAT | O_EXCL ,
S_IRUSR | S_IWUSR);
- if (be->lockfd < 0)
+ if (xml_be->m_lockfd < 0)
{
/* oops .. we can't create the lockfile .. */
switch (errno)
@@ -153,14 +153,14 @@ gnc_xml_be_get_file_lock (FileBackend* be)
case EROFS:
case ENOSPC:
PWARN ("Unable to create the lockfile %s; may not have write priv",
- be->lockfile);
+ xml_be->m_lockfile);
be_err = ERR_BACKEND_READONLY;
break;
default:
be_err = ERR_BACKEND_LOCKED;
break;
}
- qof_backend_set_error ((QofBackend*)be, be_err);
+ qof_backend_set_error ((QofBackend*)xml_be, be_err);
return FALSE;
}
@@ -181,13 +181,13 @@ gnc_xml_be_get_file_lock (FileBackend* be)
*/
#ifndef G_OS_WIN32
- pathbuf_size = strlen (be->lockfile) + 100;
+ pathbuf_size = strlen (xml_be->m_lockfile) + 100;
pathbuf = (char*) malloc (pathbuf_size);
if (pathbuf == NULL)
{
return FALSE;
}
- strcpy (pathbuf, be->lockfile);
+ strcpy (pathbuf, xml_be->m_lockfile);
path = strrchr (pathbuf, '.');
while (snprintf (path, pathbuf_size - (path - pathbuf), ".%lx.%d.LNK",
gethostid (), getpid ())
@@ -206,7 +206,7 @@ gnc_xml_be_get_file_lock (FileBackend* be)
}
}
- rc = link (be->lockfile, pathbuf);
+ rc = link (xml_be->m_lockfile, pathbuf);
if (rc)
{
/* If hard links aren't supported, just allow the lock. */
@@ -219,45 +219,45 @@ gnc_xml_be_get_file_lock (FileBackend* be)
# endif
)
{
- be->linkfile = NULL;
+ xml_be->m_linkfile = NULL;
free (pathbuf);
return TRUE;
}
/* Otherwise, something else is wrong. */
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+ qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
g_unlink (pathbuf);
free (pathbuf);
- close (be->lockfd);
- g_unlink (be->lockfile);
+ close (xml_be->m_lockfd);
+ g_unlink (xml_be->m_lockfile);
return FALSE;
}
- rc = g_stat (be->lockfile, &statbuf);
+ rc = g_stat (xml_be->m_lockfile, &statbuf);
if (rc)
{
/* oops .. stat failed! This can't happen! */
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
- qof_backend_set_message ((QofBackend*)be, "Failed to stat lockfile %s",
- be->lockfile);
+ qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
+ qof_backend_set_message ((QofBackend*)xml_be, "Failed to stat lockfile %s",
+ xml_be->m_lockfile);
g_unlink (pathbuf);
free (pathbuf);
- close (be->lockfd);
- g_unlink (be->lockfile);
+ close (xml_be->m_lockfd);
+ g_unlink (xml_be->m_lockfile);
return FALSE;
}
if (statbuf.st_nlink != 2)
{
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+ qof_backend_set_error ((QofBackend*)xml_be, ERR_BACKEND_LOCKED);
g_unlink (pathbuf);
free (pathbuf);
- close (be->lockfd);
- g_unlink (be->lockfile);
+ close (xml_be->m_lockfd);
+ g_unlink (xml_be->m_lockfile);
return FALSE;
}
- be->linkfile = g_strdup (pathbuf);
+ xml_be->m_linkfile = g_strdup (pathbuf);
free (pathbuf);
return TRUE;
@@ -265,7 +265,7 @@ gnc_xml_be_get_file_lock (FileBackend* be)
#else /* ifndef G_OS_WIN32 */
/* On windows, there is no NFS and the open(,O_CREAT | O_EXCL)
is sufficient for locking. */
- be->linkfile = NULL;
+ xml_be->m_linkfile = NULL;
return TRUE;
#endif /* ifndef G_OS_WIN32 */
}
@@ -275,40 +275,40 @@ gnc_xml_be_get_file_lock (FileBackend* be)
#define FILE_URI_PREFIX "file://"
static void
-xml_session_begin (QofBackend* be_start, QofSession* session,
+xml_session_begin (QofBackend* qof_be, QofSession* session,
const char* book_id, gboolean ignore_lock,
gboolean create, gboolean force)
{
- FileBackend* be = (FileBackend*) be_start;
+ XmlBackend* xml_be = (XmlBackend*) qof_be;
ENTER (" ");
/* Make sure the directory is there */
- be->fullpath = gnc_uri_get_path (book_id);
+ xml_be->m_fullpath = gnc_uri_get_path (book_id);
- if (NULL == be->fullpath)
+ if (NULL == xml_be->m_fullpath)
{
- qof_backend_set_error (be_start, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (be_start, "No path specified");
+ qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
+ qof_backend_set_message (qof_be, "No path specified");
LEAVE ("");
return;
}
- if (create && !force && save_may_clobber_data (be))
+ if (create && !force && save_may_clobber_data (xml_be))
{
- qof_backend_set_error (be_start, ERR_BACKEND_STORE_EXISTS);
+ qof_backend_set_error (qof_be, ERR_BACKEND_STORE_EXISTS);
LEAVE ("Might clobber, no force");
return;
}
- be->be.fullpath = be->fullpath;
- be->dirname = g_path_get_dirname (be->fullpath);
+ xml_be->be.fullpath = xml_be->m_fullpath;
+ xml_be->m_dirname = g_path_get_dirname (xml_be->m_fullpath);
{
struct stat statbuf;
int rc;
/* Again check whether the directory can be accessed */
- rc = g_stat (be->dirname, &statbuf);
+ rc = g_stat (xml_be->m_dirname, &statbuf);
if (rc != 0
#if COMPILER(MSVC)
|| (statbuf.st_mode & _S_IFDIR) == 0
@@ -319,30 +319,31 @@ xml_session_begin (QofBackend* be_start, QofSession* session,
{
/* Error on stat or if it isn't a directory means we
cannot find this filename */
- qof_backend_set_error (be_start, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (be_start, "Couldn't find directory for %s",
- be->fullpath);
- PWARN ("Couldn't find directory for %s", be->fullpath);
- g_free (be->fullpath);
- be->fullpath = NULL;
- g_free (be->dirname);
- be->dirname = NULL;
+ qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
+ qof_backend_set_message (qof_be, "Couldn't find directory for %s",
+ xml_be->m_fullpath);
+ PWARN ("Couldn't find directory for %s", xml_be->m_fullpath);
+ g_free (xml_be->m_fullpath);
+ xml_be->m_fullpath = NULL;
+ g_free (xml_be->m_dirname);
+ xml_be->m_dirname = NULL;
LEAVE ("");
return;
}
/* Now check whether we can g_stat the file itself */
- rc = g_stat (be->fullpath, &statbuf);
+ rc = g_stat (xml_be->m_fullpath, &statbuf);
if ((rc != 0) && (!create))
{
/* Error on stat means the file doesn't exist */
- qof_backend_set_error (be_start, ERR_FILEIO_FILE_NOT_FOUND);
- qof_backend_set_message (be_start, "Couldn't find %s", be->fullpath);
- PWARN ("Couldn't find %s", be->fullpath);
- g_free (be->fullpath);
- be->fullpath = NULL;
- g_free (be->dirname);
- be->dirname = NULL;
+ qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
+ qof_backend_set_message (qof_be, "Couldn't find %s",
+ xml_be->m_fullpath);
+ PWARN ("Couldn't find %s", xml_be->m_fullpath);
+ g_free (xml_be->m_fullpath);
+ xml_be->m_fullpath = NULL;
+ g_free (xml_be->m_dirname);
+ xml_be->m_dirname = NULL;
LEAVE ("");
return;
}
@@ -354,14 +355,14 @@ xml_session_begin (QofBackend* be_start, QofSession* session,
#endif
)
{
- qof_backend_set_error (be_start, ERR_FILEIO_UNKNOWN_FILE_TYPE);
- qof_backend_set_message (be_start, "Path %s is a directory",
- be->fullpath);
- PWARN ("Path %s is a directory", be->fullpath);
- g_free (be->fullpath);
- be->fullpath = NULL;
- g_free (be->dirname);
- be->dirname = NULL;
+ qof_backend_set_error (qof_be, ERR_FILEIO_UNKNOWN_FILE_TYPE);
+ qof_backend_set_message (qof_be, "Path %s is a directory",
+ xml_be->m_fullpath);
+ PWARN ("Path %s is a directory", xml_be->m_fullpath);
+ g_free (xml_be->m_fullpath);
+ xml_be->m_fullpath = NULL;
+ g_free (xml_be->m_dirname);
+ xml_be->m_dirname = NULL;
LEAVE ("");
return;
}
@@ -371,23 +372,23 @@ xml_session_begin (QofBackend* be_start, QofSession* session,
/* ---------------------------------------------------- */
/* We should now have a fully resolved path name.
* Let's start logging */
- xaccLogSetBaseName (be->fullpath);
- PINFO ("logpath=%s", be->fullpath ? be->fullpath : "(null)");
+ xaccLogSetBaseName (xml_be->m_fullpath);
+ PINFO ("logpath=%s", xml_be->m_fullpath ? xml_be->m_fullpath : "(null)");
/* And let's see if we can get a lock on it. */
- be->lockfile = g_strconcat (be->fullpath, ".LCK", NULL);
+ xml_be->m_lockfile = g_strconcat (xml_be->m_fullpath, ".LCK", NULL);
- if (!ignore_lock && !gnc_xml_be_get_file_lock (be))
+ if (!ignore_lock && !gnc_xml_be_get_file_lock (xml_be))
{
// We should not ignore the lock, but couldn't get it. The
// be_get_file_lock() already set the appropriate backend_error in this
// case, so we just return here.
- g_free (be->lockfile);
- be->lockfile = NULL;
+ g_free (xml_be->m_lockfile);
+ xml_be->m_lockfile = NULL;
if (force)
{
- QofBackendError berror = qof_backend_get_error (be_start);
+ QofBackendError berror = qof_backend_get_error (qof_be);
if (berror == ERR_BACKEND_LOCKED || berror == ERR_BACKEND_READONLY)
{
// Even though we couldn't get the lock, we were told to force
@@ -397,7 +398,7 @@ xml_session_begin (QofBackend* be_start, QofSession* session,
else
{
// Unknown error. Push it again on the error stack.
- qof_backend_set_error (be_start, berror);
+ qof_backend_set_error (qof_be, berror);
}
}
@@ -412,61 +413,61 @@ xml_session_begin (QofBackend* be_start, QofSession* session,
/* ================================================================= */
static void
-xml_session_end (QofBackend* be_start)
+xml_session_end (QofBackend* qof_be)
{
- FileBackend* be = (FileBackend*)be_start;
+ XmlBackend* xml_be = (XmlBackend*)qof_be;
ENTER (" ");
- if (be->book && qof_book_is_readonly (be->book))
+ if (xml_be->m_book && qof_book_is_readonly (xml_be->m_book))
{
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_READONLY);
+ qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
return;
}
- if (be->linkfile)
- g_unlink (be->linkfile);
+ if (xml_be->m_linkfile)
+ g_unlink (xml_be->m_linkfile);
- if (be->lockfd > 0)
- close (be->lockfd);
+ if (xml_be->m_lockfd > 0)
+ close (xml_be->m_lockfd);
- if (be->lockfile)
+ if (xml_be->m_lockfile)
{
int rv;
#ifdef G_OS_WIN32
/* On windows, we need to allow write-access before
g_unlink() can succeed */
- rv = g_chmod (be->lockfile, S_IWRITE | S_IREAD);
+ rv = g_chmod (xml_be->m_lockfile, S_IWRITE | S_IREAD);
#endif
- rv = g_unlink (be->lockfile);
+ rv = g_unlink (xml_be->m_lockfile);
if (rv)
{
- PWARN ("Error on g_unlink(%s): %d: %s", be->lockfile,
+ PWARN ("Error on g_unlink(%s): %d: %s", xml_be->m_lockfile,
errno, g_strerror (errno) ? g_strerror (errno) : "");
}
}
- g_free (be->dirname);
- be->dirname = NULL;
+ g_free (xml_be->m_dirname);
+ xml_be->m_dirname = NULL;
- g_free (be->fullpath);
- be->fullpath = NULL;
+ g_free (xml_be->m_fullpath);
+ xml_be->m_fullpath = NULL;
- g_free (be->lockfile);
- be->lockfile = NULL;
+ g_free (xml_be->m_lockfile);
+ xml_be->m_lockfile = NULL;
- g_free (be->linkfile);
- be->linkfile = NULL;
+ g_free (xml_be->m_linkfile);
+ xml_be->m_linkfile = NULL;
LEAVE (" ");
}
static void
-xml_destroy_backend (QofBackend* be)
+xml_destroy_backend (QofBackend* qof_be)
{
/* Stop transaction logging */
xaccLogSetBaseName (NULL);
- qof_backend_destroy (be);
- g_free (be);
+ qof_backend_destroy (qof_be);
+ g_free (qof_be);
}
/* ================================================================= */
@@ -541,7 +542,7 @@ copy_file (const char* orig, const char* bkup)
/* ================================================================= */
static gboolean
-gnc_int_link_or_make_backup (FileBackend* be, const char* orig,
+gnc_int_link_or_make_backup (XmlBackend* xml_be, const char* orig,
const char* bkup)
{
gboolean copy_success = FALSE;
@@ -573,7 +574,7 @@ gnc_int_link_or_make_backup (FileBackend* be, const char* orig,
if (!copy_success)
{
- qof_backend_set_error ((QofBackend*)be, ERR_FILEIO_BACKUP_ERROR);
+ qof_backend_set_error ((QofBackend*)xml_be, ERR_FILEIO_BACKUP_ERROR);
PWARN ("unable to make file backup from %s to %s: %s",
orig, bkup, g_strerror (errno) ? g_strerror (errno) : "");
return FALSE;
@@ -672,7 +673,7 @@ det_exit:
}
static gboolean
-gnc_xml_be_backup_file (FileBackend* be)
+gnc_xml_be_backup_file (XmlBackend* xml_be)
{
gboolean bkup_ret;
char* timestamp;
@@ -681,7 +682,7 @@ gnc_xml_be_backup_file (FileBackend* be)
struct stat statbuf;
int rc;
- datafile = be->fullpath;
+ datafile = xml_be->m_fullpath;
rc = g_stat (datafile, &statbuf);
if (rc)
@@ -694,7 +695,7 @@ gnc_xml_be_backup_file (FileBackend* be)
char* bin_bkup = g_new (char, strlen (datafile) + strlen (back) + 1);
strcpy (bin_bkup, datafile);
strcat (bin_bkup, back);
- bkup_ret = gnc_int_link_or_make_backup (be, datafile, bin_bkup);
+ bkup_ret = gnc_int_link_or_make_backup (xml_be, datafile, bin_bkup);
g_free (bin_bkup);
if (!bkup_ret)
{
@@ -706,7 +707,7 @@ gnc_xml_be_backup_file (FileBackend* be)
backup = g_strconcat (datafile, ".", timestamp, GNC_DATAFILE_EXT, NULL);
g_free (timestamp);
- bkup_ret = gnc_int_link_or_make_backup (be, datafile, backup);
+ bkup_ret = gnc_int_link_or_make_backup (xml_be, datafile, backup);
g_free (backup);
return bkup_ret;
@@ -715,12 +716,12 @@ gnc_xml_be_backup_file (FileBackend* be)
/* ================================================================= */
static gboolean
-gnc_xml_be_write_to_file (FileBackend* fbe,
+gnc_xml_be_write_to_file (XmlBackend* xml_be,
QofBook* book,
const gchar* datafile,
gboolean make_backup)
{
- QofBackend* be = &fbe->be;
+ QofBackend* qof_be = &xml_be->be;
char* tmp_name;
struct stat statbuf;
int rc;
@@ -731,7 +732,7 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
if (book && qof_book_is_readonly (book))
{
/* Are we read-only? Don't continue in this case. */
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_READONLY);
+ qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
LEAVE ("");
return FALSE;
}
@@ -746,15 +747,15 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
if (!mktemp (tmp_name))
{
- qof_backend_set_error (be, ERR_BACKEND_MISC);
- qof_backend_set_message (be, "Failed to make temp file");
+ qof_backend_set_error (qof_be, ERR_BACKEND_MISC);
+ qof_backend_set_message (qof_be, "Failed to make temp file");
LEAVE ("");
return FALSE;
}
if (make_backup)
{
- if (!gnc_xml_be_backup_file (fbe))
+ if (!gnc_xml_be_backup_file (xml_be))
{
LEAVE ("");
return FALSE;
@@ -774,8 +775,8 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
/* Use the permissions from the original data file */
if (g_chmod (tmp_name, statbuf.st_mode) != 0)
{
- /* qof_backend_set_error(be, ERR_BACKEND_PERM); */
- /* qof_backend_set_message( be, "Failed to chmod filename %s", tmp_name ); */
+ /* qof_backend_set_error(qof_be, ERR_BACKEND_PERM); */
+ /* qof_backend_set_message(qof_be, "Failed to chmod filename %s", tmp_name ); */
/* Even if the chmod did fail, the save
nevertheless completed successfully. It is
therefore wrong to signal the ERR_BACKEND_PERM
@@ -794,8 +795,8 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
that. */
if (chown (tmp_name, -1, statbuf.st_gid) != 0)
{
- /* qof_backend_set_error(be, ERR_BACKEND_PERM); */
- /* qof_backend_set_message( be, "Failed to chown filename %s", tmp_name ); */
+ /* qof_backend_set_error(qof_be, ERR_BACKEND_PERM); */
+ /* qof_backend_set_message(qof_be, "Failed to chown filename %s", tmp_name ); */
/* A failed chown doesn't mean that the saving itself
failed. So don't abort with an error here! */
PWARN ("unable to chown filename %s: %s",
@@ -810,7 +811,7 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
}
if (g_unlink (datafile) != 0 && errno != ENOENT)
{
- qof_backend_set_error (be, ERR_BACKEND_READONLY);
+ qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
PWARN ("unable to unlink filename %s: %s",
datafile ? datafile : "(null)",
g_strerror (errno) ? g_strerror (errno) : "");
@@ -818,10 +819,10 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
LEAVE ("");
return FALSE;
}
- if (!gnc_int_link_or_make_backup (fbe, tmp_name, datafile))
+ if (!gnc_int_link_or_make_backup (xml_be, tmp_name, datafile))
{
- qof_backend_set_error (be, ERR_FILEIO_BACKUP_ERROR);
- qof_backend_set_message (be, "Failed to make backup file %s",
+ qof_backend_set_error (qof_be, ERR_FILEIO_BACKUP_ERROR);
+ qof_backend_set_message (qof_be, "Failed to make backup file %s",
datafile ? datafile : "NULL");
g_free (tmp_name);
LEAVE ("");
@@ -829,7 +830,7 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
}
if (g_unlink (tmp_name) != 0)
{
- qof_backend_set_error (be, ERR_BACKEND_PERM);
+ qof_backend_set_error (qof_be, ERR_BACKEND_PERM);
PWARN ("unable to unlink temp filename %s: %s",
tmp_name ? tmp_name : "(null)",
g_strerror (errno) ? g_strerror (errno) : "");
@@ -862,7 +863,7 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
be_err = ERR_BACKEND_MISC;
break;
}
- qof_backend_set_error (be, be_err);
+ qof_backend_set_error (qof_be, be_err);
PWARN ("unable to unlink temp_filename %s: %s",
tmp_name ? tmp_name : "(null)",
g_strerror (errno) ? g_strerror (errno) : "");
@@ -871,8 +872,8 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
else
{
/* Use a generic write error code */
- qof_backend_set_error (be, ERR_FILEIO_WRITE_ERROR);
- qof_backend_set_message (be, "Unable to write to temp file %s",
+ qof_backend_set_error (qof_be, ERR_FILEIO_WRITE_ERROR);
+ qof_backend_set_message (qof_be, "Unable to write to temp file %s",
tmp_name ? tmp_name : "NULL");
}
g_free (tmp_name);
@@ -891,17 +892,17 @@ gnc_xml_be_write_to_file (FileBackend* fbe,
*/
static void
-gnc_xml_be_remove_old_files (FileBackend* be)
+gnc_xml_be_remove_old_files (XmlBackend* xml_be)
{
const gchar* dent;
GDir* dir;
struct stat lockstatbuf, statbuf;
time64 now;
- if (g_stat (be->lockfile, &lockstatbuf) != 0)
+ if (g_stat (xml_be->m_lockfile, &lockstatbuf) != 0)
return;
- dir = g_dir_open (be->dirname, 0, NULL);
+ dir = g_dir_open (xml_be->m_dirname, 0, NULL);
if (!dir)
return;
@@ -917,17 +918,17 @@ gnc_xml_be_remove_old_files (FileBackend* be)
g_str_has_suffix (dent, GNC_LOGFILE_EXT)))
continue;
- name = g_build_filename (be->dirname, dent, (gchar*)NULL);
+ name = g_build_filename (xml_be->m_dirname, dent, (gchar*)NULL);
/* Only evaluate files associated with the current data file. */
- if (!g_str_has_prefix (name, be->fullpath))
+ if (!g_str_has_prefix (name, xml_be->m_fullpath))
{
g_free (name);
continue;
}
/* Never remove the current data file itself */
- if (g_strcmp0 (name, be->fullpath) == 0)
+ if (g_strcmp0 (name, xml_be->m_fullpath) == 0)
{
g_free (name);
continue;
@@ -937,7 +938,7 @@ gnc_xml_be_remove_old_files (FileBackend* be)
if (g_str_has_suffix (name, ".LNK"))
{
/* Is a lock file. Skip the active lock file */
- if ((g_strcmp0 (name, be->linkfile) != 0) &&
+ if ((g_strcmp0 (name, xml_be->m_linkfile) != 0) &&
/* Only delete lock files older than the active one */
(g_stat (name, &statbuf) == 0) &&
(statbuf.st_mtime < lockstatbuf.st_mtime))
@@ -964,7 +965,7 @@ gnc_xml_be_remove_old_files (FileBackend* be)
* juggling, but considering the above tests, this should always
* be safe */
regex_t pattern;
- gchar* stamp_start = name + strlen (be->fullpath);
+ gchar* stamp_start = name + strlen (xml_be->m_fullpath);
gchar* expression = g_strdup_printf ("^\\.[[:digit:]]{14}(\\%s|\\%s|\\.xac)$",
GNC_DATAFILE_EXT, GNC_LOGFILE_EXT);
gboolean got_date_stamp = FALSE;
@@ -1018,10 +1019,10 @@ gnc_xml_be_remove_old_files (FileBackend* be)
}
static void
-xml_sync_all (QofBackend* be, QofBook* book)
+xml_sync_all (QofBackend* qof_be, QofBook* book)
{
- FileBackend* fbe = (FileBackend*) be;
- ENTER ("book=%p, fbe->book=%p", book, fbe->book);
+ XmlBackend* xml_be = (XmlBackend*) qof_be;
+ ENTER ("book=%p, xml_be->m_book=%p", book, xml_be->m_book);
/* We make an important assumption here, that we might want to change
* in the future: when the user says 'save', we really save the one,
@@ -1029,18 +1030,18 @@ xml_sync_all (QofBackend* be, QofBook* book)
* for multiple books have been removed in the meantime and there is just one
* book, no more.
*/
- if (NULL == fbe->book) fbe->book = book;
- if (book != fbe->book) return;
+ if (NULL == xml_be->m_book) xml_be->m_book = book;
+ if (book != xml_be->m_book) return;
- if (qof_book_is_readonly (fbe->book))
+ if (qof_book_is_readonly (xml_be->m_book))
{
/* Are we read-only? Don't continue in this case. */
- qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_READONLY);
+ qof_backend_set_error (qof_be, ERR_BACKEND_READONLY);
return;
}
- gnc_xml_be_write_to_file (fbe, book, fbe->fullpath, TRUE);
- gnc_xml_be_remove_old_files (fbe);
+ gnc_xml_be_write_to_file (xml_be, book, xml_be->m_fullpath, TRUE);
+ gnc_xml_be_remove_old_files (xml_be);
LEAVE ("book=%p", book);
}
@@ -1053,14 +1054,14 @@ xml_sync_all (QofBackend* be, QofBook* book)
*/
static char*
-build_period_filepath (FileBackend* fbe, QofBook* book)
+build_period_filepath (XmlBackend* xml_be, QofBook* book)
{
int len;
char* str, *p, *q;
- len = strlen (fbe->fullpath) + GUID_ENCODING_LENGTH + 14;
+ len = strlen (xml_be->m_fullpath) + GUID_ENCODING_LENGTH + 14;
str = g_new (char, len);
- strcpy (str, fbe->fullpath);
+ strcpy (str, xml_be->m_fullpath);
/* XXX it would be nice for the user if we made the book
* closing date and/or title part of the file-name. */
@@ -1069,7 +1070,7 @@ build_period_filepath (FileBackend* fbe, QofBook* book)
p = stpcpy (p, "book-");
p = guid_to_string_buff (qof_book_get_guid (book), p);
p = stpcpy (p, "-");
- q = strrchr (fbe->fullpath, G_DIR_SEPARATOR);
+ q = strrchr (xml_be->m_fullpath, G_DIR_SEPARATOR);
q++;
p = stpcpy (p, q);
p = stpcpy (p, ".gml");
@@ -1078,20 +1079,20 @@ build_period_filepath (FileBackend* fbe, QofBook* book)
}
static void
-xml_begin_edit (QofBackend* be, QofInstance* inst)
+xml_begin_edit (QofBackend* qof_be, QofInstance* inst)
{
if (0) build_period_filepath (0, 0);
#if BORKEN_FOR_NOW
- FileBackend* fbe = (FileBackend*) be;
+ XmlBackend* xml_be = (XmlBackend*) qof_be;
QofBook* book = gp;
const char* filepath;
QofIdTypeConst typ = QOF_INSTANCE (inst)->e_type;
if (strcmp (GNC_ID_PERIOD, typ)) return;
- filepath = build_period_filepath (fbe, book);
+ filepath = build_period_filepath (xml_be, book);
PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
- if (NULL == fbe->primary_book)
+ if (NULL == xml_be->m_primary_book)
{
PERR ("You should have saved the data "
"at least once before closing the books!\n");
@@ -1106,7 +1107,7 @@ xml_begin_edit (QofBackend* be, QofInstance* inst)
}
static void
-xml_rollback_edit (QofBackend* be, QofInstance* inst)
+xml_rollback_edit (QofBackend* qof_be, QofInstance* inst)
{
#if BORKEN_FOR_NOW
QofBook* book = gp;
@@ -1126,44 +1127,44 @@ xml_rollback_edit (QofBackend* be, QofInstance* inst)
way. */
static void
-gnc_xml_be_load_from_file (QofBackend* bend, QofBook* book,
+gnc_xml_be_load_from_file (QofBackend* qof_be, QofBook* book,
QofBackendLoadType loadType)
{
QofBackendError error;
gboolean rc;
- FileBackend* be = (FileBackend*) bend;
+ XmlBackend* xml_be = (XmlBackend*) qof_be;
if (loadType != LOAD_TYPE_INITIAL_LOAD) return;
error = ERR_BACKEND_NO_ERR;
- be->book = book;
+ xml_be->m_book = book;
- switch (gnc_xml_be_determine_file_type (be->fullpath))
+ switch (gnc_xml_be_determine_file_type (xml_be->m_fullpath))
{
case GNC_BOOK_XML2_FILE:
- rc = qof_session_load_from_xml_file_v2 (be, book, GNC_BOOK_XML2_FILE);
+ rc = qof_session_load_from_xml_file_v2 (xml_be, book, GNC_BOOK_XML2_FILE);
if (FALSE == rc)
{
- PWARN ("Syntax error in Xml File %s", be->fullpath);
+ PWARN ("Syntax error in Xml File %s", xml_be->m_fullpath);
error = ERR_FILEIO_PARSE_ERROR;
}
break;
case GNC_BOOK_XML2_FILE_NO_ENCODING:
error = ERR_FILEIO_NO_ENCODING;
- PWARN ("No character encoding in Xml File %s", be->fullpath);
+ PWARN ("No character encoding in Xml File %s", xml_be->m_fullpath);
break;
case GNC_BOOK_XML1_FILE:
- rc = qof_session_load_from_xml_file (book, be->fullpath);
+ rc = qof_session_load_from_xml_file (book, xml_be->m_fullpath);
if (FALSE == rc)
{
- PWARN ("Syntax error in Xml File %s", be->fullpath);
+ PWARN ("Syntax error in Xml File %s", xml_be->m_fullpath);
error = ERR_FILEIO_PARSE_ERROR;
}
break;
case GNC_BOOK_POST_XML2_0_0_FILE:
error = ERR_BACKEND_TOO_NEW;
- PWARN ("Version of Xml file %s is newer than what we can read", be->fullpath);
+ PWARN ("Version of Xml file %s is newer than what we can read", xml_be->m_fullpath);
break;
default:
/* If file type wasn't known, check errno again to give the
@@ -1189,7 +1190,7 @@ gnc_xml_be_load_from_file (QofBackend* bend, QofBook* book,
if (error != ERR_BACKEND_NO_ERR)
{
- qof_backend_set_error (bend, error);
+ qof_backend_set_error (qof_be, error);
}
/* We just got done loading, it can't possibly be dirty !! */
@@ -1199,25 +1200,25 @@ gnc_xml_be_load_from_file (QofBackend* bend, QofBook* book,
/* ---------------------------------------------------------------------- */
static gboolean
-save_may_clobber_data (FileBackend *bend)
+save_may_clobber_data (XmlBackend *xml_be)
{
struct stat statbuf;
- if (!bend->fullpath) return FALSE;
+ if (!xml_be->m_fullpath) return FALSE;
/* FIXME: Make sure this doesn't need more sophisticated semantics
* in the face of special file, devices, pipes, symlinks, etc. */
- if (g_stat (bend->fullpath, &statbuf) == 0) return TRUE;
+ if (g_stat (xml_be->m_fullpath, &statbuf) == 0) return TRUE;
return FALSE;
}
static void
-gnc_xml_be_write_accounts_to_file (QofBackend* be, QofBook* book)
+gnc_xml_be_write_accounts_to_file (QofBackend* qof_be, QofBook* book)
{
const gchar* datafile;
- datafile = ((FileBackend*)be)->fullpath;
- gnc_book_write_accounts_to_xml_file_v2 (be, book, datafile);
+ datafile = ((XmlBackend*)qof_be)->m_fullpath;
+ gnc_book_write_accounts_to_xml_file_v2 (qof_be, book, datafile);
}
/* ================================================================= */
@@ -1225,37 +1226,37 @@ gnc_xml_be_write_accounts_to_file (QofBackend* be, QofBook* book)
QofBackend*
QofXmlBackendProvider::create_backend(void)
{
- FileBackend* gnc_be;
- QofBackend* be;
+ XmlBackend* xml_be;
+ QofBackend* qof_be;
- gnc_be = g_new0 (FileBackend, 1);
- be = (QofBackend*) gnc_be;
- qof_backend_init (be);
+ xml_be = g_new0 (XmlBackend, 1);
+ qof_be = (QofBackend*) xml_be;
+ qof_backend_init (qof_be);
- be->session_begin = xml_session_begin;
- be->session_end = xml_session_end;
- be->destroy_backend = xml_destroy_backend;
+ qof_be->session_begin = xml_session_begin;
+ qof_be->session_end = xml_session_end;
+ qof_be->destroy_backend = xml_destroy_backend;
- be->load = gnc_xml_be_load_from_file;
+ qof_be->load = gnc_xml_be_load_from_file;
/* The file backend treats accounting periods transactionally. */
- be->begin = xml_begin_edit;
- be->commit = NULL;
- be->rollback = xml_rollback_edit;
+ qof_be->begin = xml_begin_edit;
+ qof_be->commit = NULL;
+ qof_be->rollback = xml_rollback_edit;
- be->sync = xml_sync_all;
+ qof_be->sync = xml_sync_all;
- be->export_fn = gnc_xml_be_write_accounts_to_file;
+ qof_be->export_fn = gnc_xml_be_write_accounts_to_file;
- gnc_be->dirname = NULL;
- gnc_be->fullpath = NULL;
- gnc_be->lockfile = NULL;
- gnc_be->linkfile = NULL;
- gnc_be->lockfd = -1;
+ xml_be->m_dirname = NULL;
+ xml_be->m_fullpath = NULL;
+ xml_be->m_lockfile = NULL;
+ xml_be->m_linkfile = NULL;
+ xml_be->m_lockfd = -1;
- gnc_be->book = NULL;
+ xml_be->m_book = NULL;
- return be;
+ return qof_be;
}
static void
diff --git a/src/backend/xml/gnc-backend-xml.h b/src/backend/xml/gnc-backend-xml.h
index 2f021cf..c5fa189 100644
--- a/src/backend/xml/gnc-backend-xml.h
+++ b/src/backend/xml/gnc-backend-xml.h
@@ -56,24 +56,6 @@ typedef enum
GNC_BOOK_POST_XML2_0_0_FILE
} QofBookFileType;
-struct FileBackend_struct
-{
- QofBackend be;
-
- char* dirname;
- char* fullpath; /* Fully qualified path to book */
- char* lockfile;
- char* linkfile;
- int lockfd;
-
- QofBook* book; /* The primary, main open book */
-};
-
-typedef struct FileBackend_struct FileBackend;
-
-// This is now a static inside the module
-//QofBackend * libgncmod_backend_file_LTX_gnc_backend_new(void);
-
/** Initialization function which can be used when this module is
* statically linked into the application. */
void gnc_module_init_backend_xml (void);
@@ -88,5 +70,20 @@ void qof_backend_module_init (void);
#endif
#ifdef __cplusplus
}
+
+class XmlBackend
+{
+public:
+ QofBackend be;
+
+ char* m_dirname;
+ char* m_fullpath; /* Fully qualified path to book */
+ char* m_lockfile;
+ char* m_linkfile;
+ int m_lockfd;
+
+ QofBook* m_book; /* The primary, main open book */
+};
+
#endif
#endif /* GNC_BACKEND_XML_H_ */
diff --git a/src/backend/xml/io-gncxml-v2.cpp b/src/backend/xml/io-gncxml-v2.cpp
index 614c4fd..2735238 100644
--- a/src/backend/xml/io-gncxml-v2.cpp
+++ b/src/backend/xml/io-gncxml-v2.cpp
@@ -692,12 +692,12 @@ gnc_sixtp_gdv2_new (
static gboolean
qof_session_load_from_xml_file_v2_full (
- FileBackend* fbe, QofBook* book,
+ XmlBackend* xml_be, QofBook* book,
sixtp_push_handler push_handler, gpointer push_user_data,
QofBookFileType type)
{
Account* root;
- QofBackend* be = &fbe->be;
+ QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(xml_be);
sixtp_gdv2* gd;
sixtp* top_parser;
sixtp* main_parser;
@@ -706,7 +706,7 @@ qof_session_load_from_xml_file_v2_full (
gboolean retval;
char* v2type = NULL;
- gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, be->percentage);
+ gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, qof_be->percentage);
top_parser = sixtp_new ();
main_parser = sixtp_new ();
@@ -793,7 +793,7 @@ qof_session_load_from_xml_file_v2_full (
* https://bugzilla.gnome.org/show_bug.cgi?id=712528 for more
* info.
*/
- gchar* filename = fbe->fullpath;
+ gchar* filename = xml_be->m_fullpath;
FILE* file;
gboolean is_compressed = is_gzipped_file (filename);
file = try_gz_open (filename, "r", is_compressed, FALSE);
@@ -864,10 +864,10 @@ bail:
}
gboolean
-qof_session_load_from_xml_file_v2 (FileBackend* fbe, QofBook* book,
+qof_session_load_from_xml_file_v2 (XmlBackend* xml_be, QofBook* book,
QofBookFileType type)
{
- return qof_session_load_from_xml_file_v2_full (fbe, book, NULL, NULL, type);
+ return qof_session_load_from_xml_file_v2_full (xml_be, book, NULL, NULL, type);
}
/***********************************************************************/
@@ -1257,21 +1257,21 @@ static void
write_budget (QofInstance* ent, gpointer data)
{
xmlNodePtr node;
- struct file_backend* be = static_cast<decltype (be)> (data);
+ struct file_backend* file_be = static_cast<decltype (file_be)> (data);
GncBudget* bgt = GNC_BUDGET (ent);
- if (ferror (be->out))
+ if (ferror (file_be->out))
return;
node = gnc_budget_dom_tree_create (bgt);
- xmlElemDump (be->out, NULL, node);
+ xmlElemDump (file_be->out, NULL, node);
xmlFreeNode (node);
- if (ferror (be->out) || fprintf (be->out, "\n") < 0)
+ if (ferror (file_be->out) || fprintf (file_be->out, "\n") < 0)
return;
- be->gd->counter.budgets_loaded++;
- sixtp_run_callback (be->gd, "budgets");
+ file_be->gd->counter.budgets_loaded++;
+ sixtp_run_callback (file_be->gd, "budgets");
}
gboolean
@@ -1328,7 +1328,7 @@ write_v2_header (FILE* out)
gboolean
gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
{
- QofBackend* be;
+ QofBackend* qof_be;
sixtp_gdv2* gd;
gboolean success = TRUE;
@@ -1338,8 +1338,8 @@ gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
|| !write_counts (out, "book", 1, NULL))
return FALSE;
- be = qof_book_get_backend (book);
- gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, be->percentage);
+ qof_be = qof_book_get_backend (book);
+ gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, qof_be->percentage);
gd->counter.commodities_total =
gnc_commodity_table_get_size (gnc_commodity_table_get_table (book));
gd->counter.accounts_total = 1 +
@@ -1364,7 +1364,7 @@ gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
* This function is called by the "export" code.
*/
gboolean
-gnc_book_write_accounts_to_xml_filehandle_v2 (QofBackend* be, QofBook* book,
+gnc_book_write_accounts_to_xml_filehandle_v2 (QofBackend* qof_be, QofBook* book,
FILE* out)
{
gnc_commodity_table* table;
@@ -1385,7 +1385,7 @@ gnc_book_write_accounts_to_xml_filehandle_v2 (QofBackend* be, QofBook* book,
|| !write_counts (out, "commodity", ncom, "account", nacc, NULL))
return FALSE;
- gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback, be->percentage);
+ gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback, qof_be->percentage);
gd->counter.commodities_total = ncom;
gd->counter.accounts_total = nacc;
@@ -1651,7 +1651,7 @@ gnc_book_write_to_xml_file_v2 (
*/
gboolean
gnc_book_write_accounts_to_xml_file_v2 (
- QofBackend* be,
+ QofBackend* qof_be,
QofBook* book,
const char* filename)
{
@@ -1662,7 +1662,7 @@ gnc_book_write_accounts_to_xml_file_v2 (
/* Try to write as much as possible */
if (!out
- || !gnc_book_write_accounts_to_xml_filehandle_v2 (be, book, out)
+ || !gnc_book_write_accounts_to_xml_filehandle_v2 (qof_be, book, out)
|| !write_emacs_trailer (out))
success = FALSE;
@@ -1670,11 +1670,11 @@ gnc_book_write_accounts_to_xml_file_v2 (
if (out && fclose (out))
success = FALSE;
- if (!success && !qof_backend_check_error (be))
+ if (!success && !qof_backend_check_error (qof_be))
{
/* Use a generic write error code */
- qof_backend_set_error (be, ERR_FILEIO_WRITE_ERROR);
+ qof_backend_set_error (qof_be, ERR_FILEIO_WRITE_ERROR);
}
return success;
@@ -2168,17 +2168,17 @@ cleanup_push_handler:
}
gboolean
-gnc_xml2_parse_with_subst (FileBackend* fbe, QofBook* book, GHashTable* subst)
+gnc_xml2_parse_with_subst (XmlBackend* xml_be, QofBook* book, GHashTable* subst)
{
push_data_type* push_data;
gboolean success;
push_data = g_new (push_data_type, 1);
- push_data->filename = fbe->fullpath;
+ push_data->filename = xml_be->m_fullpath;
push_data->subst = subst;
success = qof_session_load_from_xml_file_v2_full (
- fbe, book, (sixtp_push_handler) parse_with_subst_push_handler,
+ xml_be, book, (sixtp_push_handler) parse_with_subst_push_handler,
push_data, GNC_BOOK_XML2_FILE);
if (success)
diff --git a/src/backend/xml/io-gncxml-v2.h b/src/backend/xml/io-gncxml-v2.h
index 564e4f7..8948b05 100644
--- a/src/backend/xml/io-gncxml-v2.h
+++ b/src/backend/xml/io-gncxml-v2.h
@@ -43,6 +43,7 @@ extern "C"
#include "sixtp.h"
#include <vector>
+class XmlBackend;
/**
* Struct used to pass in a new data type for XML storage. This contains
@@ -84,7 +85,7 @@ typedef struct
} gnc_template_xaction_data;
/** read in an account group from a file */
-gboolean qof_session_load_from_xml_file_v2 (FileBackend*, QofBook*,
+gboolean qof_session_load_from_xml_file_v2 (XmlBackend*, QofBook*,
QofBookFileType);
/* write all book info to a file */
@@ -154,7 +155,7 @@ gint gnc_xml2_find_ambiguous (
* @param subst hash table with keys and values of type gchar*
*/
gboolean gnc_xml2_parse_with_subst (
- FileBackend* fbe, QofBook* book, GHashTable* subst);
+ XmlBackend* xml_be, QofBook* book, GHashTable* subst);
#ifdef __cplusplus
}
typedef struct
diff --git a/src/gnome-utils/assistant-xml-encoding.c b/src/gnome-utils/assistant-xml-encoding.c
index 5f20b10..adf7ee7 100644
--- a/src/gnome-utils/assistant-xml-encoding.c
+++ b/src/gnome-utils/assistant-xml-encoding.c
@@ -35,8 +35,27 @@
#include "gnc-uri-utils.h"
#include "gnc-module.h"
#include "gnc-ui.h"
-#include "io-gncxml-v2.h"
+/* The following are copied from src/backend/xml/io-gncxml2-v2.h as a temporary
+ * measure to enable this to compile in the face of making changing struct
+ * FileBackend into C++ class XmlBackend, which can't be exposed to this C
+ * file. A future commit will separate the session code from the UI code in this
+ * file.
+ */
+typedef struct
+{
+ GQuark encoding;
+ gchar* utf8_string;
+} conv_type;
+
+extern gint gnc_xml2_find_ambiguous (const gchar* filename,
+ GList* encodings,
+ GHashTable** unique,
+ GHashTable** ambiguous,
+ GList** impossible);
+
+extern gboolean gnc_xml2_parse_with_subst (QofBackend* xml_be, QofBook* book,
+ GHashTable* subst);
/* NOTE: This file uses the term "encoding" even in places where it is not
* accurate. Please ignore that. Encodings occur in different forms:
* - as descriptive string, as in the list of system encodings
@@ -1041,7 +1060,7 @@ gxi_parse_file (GncXmlImportData *data)
{
QofSession *session = NULL;
QofBook *book;
- FileBackend *backend;
+ QofBackend *backend;
QofBackendError io_err = ERR_BACKEND_NO_ERR;
gchar *message = NULL;
gboolean success = FALSE;
@@ -1091,7 +1110,7 @@ gxi_parse_file (GncXmlImportData *data)
qof_session_pop_error (session);
book = qof_session_get_book (session);
- backend = (FileBackend*) qof_book_get_backend (book);
+ backend = qof_book_get_backend (book);
gxi_update_progress_bar (_("Parsing file..."), 0.0);
success = gnc_xml2_parse_with_subst (backend, book, data->subst);
commit 526834037cf7dfb5ab0cafc8212da9239abdb167
Author: John Ralls <jralls at ceridwen.us>
Date: Thu Nov 17 16:47:57 2016 -0800
Remove tabs from io-gncxml-v2.h.
diff --git a/src/backend/xml/io-gncxml-v2.h b/src/backend/xml/io-gncxml-v2.h
index 0457c8c..564e4f7 100644
--- a/src/backend/xml/io-gncxml-v2.h
+++ b/src/backend/xml/io-gncxml-v2.h
@@ -159,15 +159,15 @@ gboolean gnc_xml2_parse_with_subst (
}
typedef struct
{
- int version; /* backend version number */
- const char * type_name; /* The XML tag for this type */
-
- sixtp * (*create_parser) (void);
- gboolean (*add_item)(sixtp_gdv2 *, gpointer obj);
- int (*get_count) (QofBook *);
- gboolean (*write) (FILE*, QofBook*);
- void (*scrub) (QofBook *);
- gboolean (*ns) (FILE*);
+ int version; /* backend version number */
+ const char * type_name; /* The XML tag for this type */
+
+ sixtp * (*create_parser) (void);
+ gboolean (*add_item)(sixtp_gdv2 *, gpointer obj);
+ int (*get_count) (QofBook *);
+ gboolean (*write) (FILE*, QofBook*);
+ void (*scrub) (QofBook *);
+ gboolean (*ns) (FILE*);
} GncXmlDataType_t;
void gnc_xml_register_backend(GncXmlDataType_t&);
Summary of changes:
po/POTFILES.in | 2 +-
src/backend/dbi/gnc-backend-dbi.cpp | 338 +++---
src/backend/dbi/gnc-backend-dbi.hpp | 23 +-
src/backend/dbi/gnc-dbisqlconnection.cpp | 59 +-
src/backend/dbi/gnc-dbisqlconnection.hpp | 6 +-
src/backend/sql/gnc-sql-backend.cpp | 27 +-
src/backend/sql/gnc-sql-backend.hpp | 66 +-
src/backend/sql/gnc-sql-column-table-entry.hpp | 1 -
src/backend/sql/test/utest-gnc-backend-sql.cpp | 23 +-
src/backend/xml/CMakeLists.txt | 2 +
src/backend/xml/Makefile.am | 2 +
src/backend/xml/gnc-backend-xml.cpp | 1094 +-------------------
src/backend/xml/gnc-backend-xml.h | 20 +-
src/backend/xml/gnc-xml-backend.cpp | 896 ++++++++++++++++
src/backend/xml/gnc-xml-backend.hpp | 66 ++
src/backend/xml/io-gncxml-v2.cpp | 57 +-
src/backend/xml/io-gncxml-v2.h | 23 +-
src/backend/xml/test/test-dom-converters1.cpp | 2 +-
src/backend/xml/test/test-save-in-lang.cpp | 3 +-
src/backend/xml/test/test-string-converters.cpp | 2 +-
src/backend/xml/test/test-xml-transaction.cpp | 2 +-
src/engine/test/utest-Transaction.cpp | 86 +-
src/gnome-utils/assistant-xml-encoding.c | 25 +-
src/libqof/CMakeLists.txt | 4 +-
src/libqof/qof/Makefile.am | 4 +-
src/libqof/qof/qof-backend.cpp | 171 +++
src/libqof/qof/{qofbackend-p.h => qof-backend.hpp} | 305 +++---
src/libqof/qof/qofbackend.cpp | 284 -----
src/libqof/qof/qofbackend.h | 36 +-
src/libqof/qof/qofbook.cpp | 2 +-
src/libqof/qof/qofinstance.cpp | 25 +-
src/libqof/qof/qofquery.cpp | 2 +-
src/libqof/qof/qofsession.cpp | 133 ++-
src/libqof/qof/qofutil.cpp | 4 +-
src/libqof/qof/test/Makefile.am | 2 -
src/libqof/qof/test/test-qofinstance.cpp | 244 ++---
src/libqof/qof/test/test-qofsession.cpp | 46 +-
37 files changed, 1869 insertions(+), 2218 deletions(-)
create mode 100644 src/backend/xml/gnc-xml-backend.cpp
create mode 100644 src/backend/xml/gnc-xml-backend.hpp
create mode 100644 src/libqof/qof/qof-backend.cpp
rename src/libqof/qof/{qofbackend-p.h => qof-backend.hpp} (62%)
delete mode 100644 src/libqof/qof/qofbackend.cpp
More information about the gnucash-changes
mailing list