mirror of https://github.com/djcb/mu.git
* mu-store: many cleanups, fixes
This commit is contained in:
parent
5e9ea1fa23
commit
6a13866235
|
@ -33,6 +33,19 @@
|
|||
#include "mu-store.h"
|
||||
#include "mu-contacts.h"
|
||||
|
||||
|
||||
class MuStoreError {
|
||||
public:
|
||||
MuStoreError (MuError err, const std::string& what) :
|
||||
_err (err), _what(what) {}
|
||||
MuError mu_error () const { return _err; }
|
||||
const std::string& what() const { return _what; }
|
||||
private:
|
||||
MuError _err;
|
||||
const std::string _what;
|
||||
};
|
||||
|
||||
|
||||
struct _MuStore {
|
||||
|
||||
/* by default, use transactions of 30000 messages */
|
||||
|
@ -40,34 +53,44 @@ struct _MuStore {
|
|||
/* http://article.gmane.org/gmane.comp.search.xapian.general/3656 */
|
||||
#define MU_STORE_MAX_TERM_LENGTH 240
|
||||
|
||||
_MuStore (const char *xpath, const char *contacts_cache, bool read_only):
|
||||
_MuStore (const char *path, const char *contacts_path, bool read_only):
|
||||
_in_transaction (false), _processed (0),
|
||||
_batch_size(MU_STORE_DEFAULT_BATCH_SIZE),
|
||||
_contacts(0), _version(0), _db(0), _read_only(read_only) {
|
||||
_contacts(0), _path (0), _version(0),
|
||||
_db(0), _read_only(read_only), _ref_count (1) {
|
||||
|
||||
if (!check_path ())
|
||||
throw MuStoreError (MU_ERROR_FILE, "invalid_path");
|
||||
|
||||
_path = g_strdup (path);
|
||||
|
||||
if (read_only)
|
||||
_db = new Xapian::Database (xpath);
|
||||
_db = new Xapian::Database (path);
|
||||
else
|
||||
_db = new Xapian::WritableDatabase (xpath,
|
||||
Xapian::DB_CREATE_OR_OPEN);
|
||||
_db = new Xapian::WritableDatabase
|
||||
(path, Xapian::DB_CREATE_OR_OPEN);
|
||||
|
||||
if (!check_version ())
|
||||
throw std::runtime_error
|
||||
("xapian db version check failed");
|
||||
throw MuStoreError (MU_ERROR_XAPIAN_NOT_UP_TO_DATE,
|
||||
("xapian db version check failed"));
|
||||
|
||||
if (contacts_cache) {
|
||||
_contacts = mu_contacts_new (contacts_cache);
|
||||
if (contacts_path) {
|
||||
_contacts = mu_contacts_new (contacts_path);
|
||||
if (!_contacts) /* don't bail-out for this */
|
||||
throw std::runtime_error
|
||||
("failed to init contacts cache");
|
||||
throw MuStoreError (MU_ERROR_FILE,
|
||||
("failed to init contacts cache"));
|
||||
}
|
||||
|
||||
MU_WRITE_LOG ("%s: opened %s (batch size: %u)",
|
||||
__FUNCTION__, xpath, batch_size());
|
||||
__FUNCTION__, this->path(), batch_size());
|
||||
}
|
||||
|
||||
~_MuStore () {
|
||||
try {
|
||||
if (_ref_count != 0)
|
||||
g_warning ("ref count != 0");
|
||||
|
||||
g_free (_version);
|
||||
g_free (_path);
|
||||
|
||||
mu_contacts_destroy (_contacts);
|
||||
|
||||
|
@ -81,24 +104,13 @@ struct _MuStore {
|
|||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
/* get a unique id for this message; note, this function returns a
|
||||
* static buffer -- not reentrant */
|
||||
const char* get_message_uid (const char* path) {
|
||||
char pfx = 0;
|
||||
static char buf[PATH_MAX + 10];
|
||||
if (G_UNLIKELY(!pfx)) {
|
||||
pfx = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_PATH);
|
||||
buf[0]=pfx;
|
||||
}
|
||||
std::strcpy (buf + 1, path);
|
||||
return buf;
|
||||
}
|
||||
/* close the old database, and write an empty one on top of it */
|
||||
void clear();
|
||||
|
||||
/* get a unique id for this message; note, this function returns a
|
||||
* static buffer -- not reentrant */
|
||||
const char* get_message_uid (MuMsg *msg) {
|
||||
return get_message_uid (mu_msg_get_path(msg));
|
||||
}
|
||||
* static buffer -- not reentrant */
|
||||
const char* get_message_uid (const char* path);
|
||||
const char* get_message_uid (MuMsg *msg);
|
||||
|
||||
MuContacts* contacts() { return _contacts; }
|
||||
|
||||
|
@ -108,38 +120,20 @@ struct _MuStore {
|
|||
mu_store_get_metadata (this, MU_STORE_VERSION_KEY);
|
||||
}
|
||||
|
||||
void begin_transaction () {
|
||||
try {
|
||||
db_writable()->begin_transaction();
|
||||
set_in_transaction (true);
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
|
||||
void commit_transaction () {
|
||||
try {
|
||||
set_in_transaction (false);
|
||||
db_writable()->commit_transaction();
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
void rollback_transaction () {
|
||||
try {
|
||||
set_in_transaction (false);
|
||||
db_writable()->cancel_transaction();
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
void begin_transaction ();
|
||||
void commit_transaction ();
|
||||
void rollback_transaction ();
|
||||
|
||||
Xapian::WritableDatabase* db_writable() {
|
||||
if (G_UNLIKELY(_read_only))
|
||||
throw std::runtime_error
|
||||
("database is read-only");
|
||||
if (G_UNLIKELY(is_read_only()))
|
||||
throw std::runtime_error ("database is read-only");
|
||||
return (Xapian::WritableDatabase*)_db;
|
||||
}
|
||||
|
||||
Xapian::Database* db_read_only() const {
|
||||
return _db;
|
||||
}
|
||||
Xapian::Database* db_read_only() const { return _db; }
|
||||
|
||||
const char* path () const { return _path; }
|
||||
bool is_read_only () const { return _read_only; }
|
||||
|
||||
size_t batch_size () const { return _batch_size;}
|
||||
size_t set_batch_size (size_t n) {
|
||||
|
@ -153,31 +147,15 @@ struct _MuStore {
|
|||
int set_processed (int n) { return _processed = n;}
|
||||
int inc_processed () { return ++_processed; }
|
||||
|
||||
/* MuStore is ref-counted */
|
||||
guint ref () { return ++_ref_count; }
|
||||
guint unref () { return --_ref_count; }
|
||||
|
||||
private:
|
||||
|
||||
bool check_version () {
|
||||
const gchar *version;
|
||||
version = mu_store_version (this);
|
||||
|
||||
/* no version yet? it must be a new db then; we'll set the version */
|
||||
if (!version) {
|
||||
if (!mu_store_set_metadata (this, MU_STORE_VERSION_KEY,
|
||||
MU_XAPIAN_DB_VERSION)) {
|
||||
g_warning ("failed to set database version");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE; /* ok, done. */
|
||||
}
|
||||
|
||||
/* we have a version, but is it the right one? */
|
||||
if (std::strcmp (version, MU_XAPIAN_DB_VERSION) != 0) {
|
||||
g_warning ("expected db version %s, but got %s",
|
||||
MU_XAPIAN_DB_VERSION,
|
||||
version ? version : "<none>" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
bool check_version ();
|
||||
bool check_path () {
|
||||
return true; // FIXME
|
||||
}
|
||||
|
||||
/* transaction handling */
|
||||
|
@ -187,10 +165,14 @@ private:
|
|||
|
||||
/* contacts object to cache all the contact information */
|
||||
MuContacts *_contacts;
|
||||
|
||||
char *_path;
|
||||
mutable char *_version;
|
||||
|
||||
Xapian::Database *_db;
|
||||
bool _read_only;
|
||||
|
||||
guint _ref_count;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,51 @@
|
|||
#include "mu-contacts.h"
|
||||
|
||||
|
||||
bool
|
||||
_MuStore::check_version ()
|
||||
{
|
||||
const gchar *version;
|
||||
version = mu_store_version (this);
|
||||
|
||||
/* no version yet? it must be a new db then; we'll set the version */
|
||||
if (!version) {
|
||||
if (!mu_store_set_metadata (this, MU_STORE_VERSION_KEY,
|
||||
MU_STORE_SCHEMA_VERSION)) {
|
||||
g_warning ("failed to set database version");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE; /* ok, done. */
|
||||
}
|
||||
|
||||
/* we have a version, but is it the right one? */
|
||||
return mu_store_needs_upgrade (this) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* get a unique id for this message; note, this function returns a
|
||||
* static buffer -- not reentrant */
|
||||
const char*
|
||||
_MuStore::get_message_uid (const char* path) {
|
||||
char pfx = 0;
|
||||
static char buf[PATH_MAX + 10];
|
||||
if (G_UNLIKELY(!pfx)) {
|
||||
pfx = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_PATH);
|
||||
buf[0]=pfx;
|
||||
}
|
||||
std::strcpy (buf + 1, path);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* get a unique id for this message; note, this function returns a
|
||||
* static buffer -- not reentrant */
|
||||
const char*
|
||||
_MuStore::get_message_uid (MuMsg *msg) {
|
||||
return get_message_uid (mu_msg_get_path(msg));
|
||||
}
|
||||
|
||||
|
||||
|
||||
MuStore*
|
||||
mu_store_new_read_only (const char* xpath, GError **err)
|
||||
{
|
||||
|
@ -48,12 +93,28 @@ mu_store_new_read_only (const char* xpath, GError **err)
|
|||
try {
|
||||
return new _MuStore (xpath, NULL, true);
|
||||
|
||||
} catch (const MuStoreError& merr) {
|
||||
|
||||
g_set_error (err, 0, merr.mu_error(), "%s",
|
||||
merr.what().c_str());
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK_G_ERROR(err,MU_ERROR_XAPIAN);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_store_is_read_only (MuStore *store)
|
||||
{
|
||||
g_return_val_if_fail (store, FALSE);
|
||||
|
||||
try {
|
||||
return store->is_read_only() ? TRUE : FALSE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
unsigned
|
||||
mu_store_count (MuStore *store)
|
||||
|
@ -63,9 +124,7 @@ mu_store_count (MuStore *store)
|
|||
try {
|
||||
return store->db_read_only()->get_doccount();
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return 0;
|
||||
} MU_XAPIAN_CATCH_BLOCK_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,6 +136,16 @@ mu_store_version (MuStore *store)
|
|||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_store_needs_upgrade (MuStore *store)
|
||||
{
|
||||
g_return_val_if_fail (store, TRUE);
|
||||
|
||||
return (g_strcmp0 (mu_store_version (store),
|
||||
MU_STORE_SCHEMA_VERSION) == 0) ? FALSE : TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
mu_store_get_metadata (MuStore *store, const char *key)
|
||||
|
@ -88,9 +157,7 @@ mu_store_get_metadata (MuStore *store, const char *key)
|
|||
const std::string val (store->db_read_only()->get_metadata (key));
|
||||
return val.empty() ? NULL : g_strdup (val.c_str());
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return NULL;
|
||||
} MU_XAPIAN_CATCH_BLOCK_RETURN(NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -170,3 +237,5 @@ mu_store_foreach (MuStore *self,
|
|||
|
||||
return MU_OK;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,51 @@
|
|||
#include "mu-flags.h"
|
||||
#include "mu-contacts.h"
|
||||
|
||||
void
|
||||
_MuStore::begin_transaction ()
|
||||
{
|
||||
try {
|
||||
db_writable()->begin_transaction();
|
||||
set_in_transaction (true);
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_MuStore::commit_transaction () {
|
||||
try {
|
||||
set_in_transaction (false);
|
||||
db_writable()->commit_transaction();
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
void
|
||||
_MuStore::rollback_transaction () {
|
||||
try {
|
||||
set_in_transaction (false);
|
||||
db_writable()->cancel_transaction();
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* close the old database, and write an empty one on top of it */
|
||||
void
|
||||
_MuStore::clear ()
|
||||
{
|
||||
if (is_read_only())
|
||||
throw std::runtime_error ("database is read-only");
|
||||
|
||||
// clear the database
|
||||
db_writable()->close ();
|
||||
delete _db;
|
||||
_db = new Xapian::WritableDatabase
|
||||
(path(), Xapian::DB_CREATE_OR_OVERWRITE);
|
||||
|
||||
// clear the contacts cache
|
||||
if (_contacts)
|
||||
mu_contacts_clear (_contacts);
|
||||
}
|
||||
|
||||
|
||||
/* we cache these prefix strings, so we don't have to allocate the all
|
||||
|
@ -110,6 +155,10 @@ mu_store_new_writable (const char* xpath, const char *contacts_cache,
|
|||
|
||||
return store;
|
||||
|
||||
} catch (const MuStoreError& merr) {
|
||||
g_set_error (err, 0, merr.mu_error(),
|
||||
"%s", merr.what().c_str());
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK_G_ERROR(err,MU_ERROR_XAPIAN);
|
||||
|
||||
return NULL;
|
||||
|
@ -142,6 +191,19 @@ mu_store_set_metadata (MuStore *store, const char *key, const char *val)
|
|||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_store_clear (MuStore *store)
|
||||
{
|
||||
g_return_val_if_fail (store, FALSE);
|
||||
|
||||
try {
|
||||
store->clear();
|
||||
return TRUE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mu_store_flush (MuStore *store)
|
||||
{
|
||||
|
|
|
@ -43,12 +43,29 @@
|
|||
#include "mu-contacts.h"
|
||||
|
||||
|
||||
void
|
||||
mu_store_destroy (MuStore *store)
|
||||
|
||||
MuStore*
|
||||
mu_store_ref (MuStore *store)
|
||||
{
|
||||
try { delete store; } MU_XAPIAN_CATCH_BLOCK;
|
||||
g_return_val_if_fail (store, NULL);
|
||||
store->ref();
|
||||
return store;
|
||||
}
|
||||
|
||||
MuStore*
|
||||
mu_store_unref (MuStore *store)
|
||||
{
|
||||
g_return_val_if_fail (store, NULL);
|
||||
|
||||
if (store->unref() == 0) {
|
||||
try { delete store; } MU_XAPIAN_CATCH_BLOCK;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static char*
|
||||
xapian_get_metadata (const gchar *xpath, const gchar *key)
|
||||
|
@ -79,76 +96,7 @@ mu_store_database_version (const gchar *xpath)
|
|||
return xapian_get_metadata (xpath, MU_STORE_VERSION_KEY);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_store_database_needs_upgrade (const gchar *xpath)
|
||||
{
|
||||
char *version;
|
||||
gboolean rv;
|
||||
|
||||
g_return_val_if_fail (xpath, TRUE);
|
||||
|
||||
version = mu_store_database_version (xpath);
|
||||
|
||||
if (g_strcmp0 (version, MU_XAPIAN_DB_VERSION) == 0)
|
||||
rv = FALSE;
|
||||
else
|
||||
rv = TRUE;
|
||||
|
||||
g_free (version);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_store_database_is_empty (const gchar* xpath)
|
||||
{
|
||||
g_return_val_if_fail (xpath, TRUE);
|
||||
|
||||
/* it's 'empty' (non-existant) */
|
||||
if (access(xpath, F_OK) != 0 && errno == ENOENT)
|
||||
return TRUE;
|
||||
|
||||
try {
|
||||
Xapian::Database db (xpath);
|
||||
return db.get_doccount() == 0 ? TRUE : FALSE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_store_database_clear (const gchar *xpath, const char *ccache)
|
||||
{
|
||||
g_return_val_if_fail (xpath, FALSE);
|
||||
g_return_val_if_fail (ccache, FALSE);
|
||||
|
||||
try {
|
||||
int rv;
|
||||
|
||||
/* clear the database */
|
||||
Xapian::WritableDatabase db
|
||||
(xpath, Xapian::DB_CREATE_OR_OVERWRITE);
|
||||
db.flush ();
|
||||
MU_WRITE_LOG ("emptied database %s", xpath);
|
||||
|
||||
/* clear the contacts cache; this is not totally
|
||||
* fail-safe, as some other process may still have it
|
||||
* open... */
|
||||
rv = unlink (ccache);
|
||||
if (rv != 0 && errno != ENOENT) {
|
||||
g_warning ("failed to remove contacts-cache: %s",
|
||||
strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
|
|
|
@ -38,7 +38,8 @@ typedef struct _MuStore MuStore;
|
|||
* @param ccachepath path where to cache the contacts information, or NULL
|
||||
* @param err to receive error info or NULL. err->code is MuError value
|
||||
*
|
||||
* @return a new MuStore object, or NULL in case of error
|
||||
* @return a new MuStore object with ref count == 1, or NULL in case
|
||||
* of error; free with mu_store_unref
|
||||
*/
|
||||
MuStore* mu_store_new_writable (const char *xpath, const char *ccachepath,
|
||||
GError **err)
|
||||
|
@ -51,18 +52,32 @@ MuStore* mu_store_new_writable (const char *xpath, const char *ccachepath,
|
|||
* @param path the path to the database
|
||||
* @param err to receive error info or NULL. err->code is MuError value
|
||||
*
|
||||
* @return a new MuStore object, or NULL in case of error
|
||||
* @return a new MuStore object with ref count == 1, or NULL in case
|
||||
* of error; free with mu_store_unref
|
||||
*/
|
||||
MuStore* mu_store_new_read_only (const char* xpath, GError **err)
|
||||
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* destroy the MuStore object and free resources
|
||||
* increase the reference count for this store with 1
|
||||
*
|
||||
* @param store a valid store, or NULL
|
||||
* @param store a valid store object
|
||||
*
|
||||
* @return the same store with increased ref count, or NULL in case of
|
||||
* error
|
||||
*/
|
||||
void mu_store_destroy (MuStore *store);
|
||||
MuStore* mu_store_ref (MuStore *store);
|
||||
|
||||
/**
|
||||
* decrease the reference count for this store with 1
|
||||
*
|
||||
* @param store a valid store object
|
||||
*
|
||||
* @return NULL
|
||||
*/
|
||||
MuStore* mu_store_unref (MuStore *store);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -116,6 +131,9 @@ XapianDatabase* mu_store_get_read_only_database (MuStore *store);
|
|||
void mu_store_set_batch_size (MuStore *store, guint batchsize);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* get the numbers of documents in the database
|
||||
*
|
||||
|
@ -221,7 +239,15 @@ time_t mu_store_get_timestamp (MuStore *store,
|
|||
const char* msgpath);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* check whether this store is read-only
|
||||
*
|
||||
* @param store a store
|
||||
*
|
||||
* @return TRUE if the store is read-only, FALSE otherwise (and in
|
||||
* case of error)
|
||||
*/
|
||||
gboolean mu_store_is_read_only (MuStore *store);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -263,10 +289,9 @@ char* mu_store_get_metadata (MuStore *store, const char *key)
|
|||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
" * get the version of the xapian database (ie., the version of the
|
||||
* 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION,
|
||||
* 'schema' we are using). If this version != MU_STORE_SCHEMA_VERSION,
|
||||
* it's means we need to a full reindex.
|
||||
*
|
||||
* @param xpath path to the xapian database
|
||||
|
@ -281,35 +306,24 @@ gchar* mu_store_database_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT;
|
|||
* check whether the database needs to be upgraded, e.g., when it was
|
||||
* created with a different version of mu
|
||||
*
|
||||
* @param xpath path to the database dir
|
||||
* @param store a MuStore instance
|
||||
*
|
||||
* @return TRUE if the database needs upgrading, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_store_database_needs_upgrade (const gchar *xpath);
|
||||
gboolean mu_store_needs_upgrade (MuStore *store);
|
||||
|
||||
|
||||
/**
|
||||
* check whether the database is empty (contains 0 documents); in
|
||||
* addition, a non-existing database is considered 'empty' too
|
||||
*
|
||||
* @param xpath path to the xapian database
|
||||
*
|
||||
* @return TRUE if the database is empty, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_store_database_is_empty (const gchar *xpath);
|
||||
|
||||
/**
|
||||
* clear the database, ie., remove all of the contents. This is a
|
||||
* destructive operation, but the database can be restored be doing a
|
||||
* full scan of the maildirs. Also, clear the contacts cache file
|
||||
*
|
||||
* @param xpath path to the database
|
||||
* @param ccache path to the contacts cache file
|
||||
* @param store a MuStore object
|
||||
*
|
||||
* @return TRUE if the clearing succeeded, FALSE otherwise.
|
||||
*/
|
||||
gboolean mu_store_database_clear (const gchar *xpath,
|
||||
const gchar *ccache);
|
||||
gboolean mu_store_clear (MuStore *store);
|
||||
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue