mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
util: remove virKeyFile
The functionality is now provided by glib's GKeyFile. Signed-off-by: Rafael Fonseca <r4f4rfs@gmail.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
5084091a5a
commit
8ffc7e9190
@ -259,7 +259,6 @@
|
||||
@SRCDIR@/src/util/viriptables.c
|
||||
@SRCDIR@/src/util/viriscsi.c
|
||||
@SRCDIR@/src/util/virjson.c
|
||||
@SRCDIR@/src/util/virkeyfile.c
|
||||
@SRCDIR@/src/util/virlease.c
|
||||
@SRCDIR@/src/util/virlockspace.c
|
||||
@SRCDIR@/src/util/virlog.c
|
||||
|
@ -2378,16 +2378,6 @@ virKeycodeValueFromString;
|
||||
virKeycodeValueTranslate;
|
||||
|
||||
|
||||
# util/virkeyfile.h
|
||||
virKeyFileFree;
|
||||
virKeyFileGetValueString;
|
||||
virKeyFileHasGroup;
|
||||
virKeyFileHasValue;
|
||||
virKeyFileLoadData;
|
||||
virKeyFileLoadFile;
|
||||
virKeyFileNew;
|
||||
|
||||
|
||||
# util/virkmod.h
|
||||
virKModConfig;
|
||||
virKModIsBlacklisted;
|
||||
|
@ -108,8 +108,6 @@ UTIL_SOURCES = \
|
||||
util/virjson.h \
|
||||
util/virkeycode.c \
|
||||
util/virkeycode.h \
|
||||
util/virkeyfile.c \
|
||||
util/virkeyfile.h \
|
||||
util/virlease.c \
|
||||
util/virlease.h \
|
||||
util/virlockspace.c \
|
||||
|
@ -1,354 +0,0 @@
|
||||
/*
|
||||
* virkeyfile.c: "ini"-style configuration file handling
|
||||
*
|
||||
* Copyright (C) 2012-2014 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
|
||||
#include "virlog.h"
|
||||
#include "viralloc.h"
|
||||
#include "virfile.h"
|
||||
#include "virutil.h"
|
||||
#include "virhash.h"
|
||||
#include "virkeyfile.h"
|
||||
#include "virerror.h"
|
||||
#include "virstring.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_CONF
|
||||
|
||||
VIR_LOG_INIT("util.keyfile");
|
||||
|
||||
typedef struct _virKeyFileGroup virKeyFileGroup;
|
||||
typedef virKeyFileGroup *virKeyFileGroupPtr;
|
||||
|
||||
typedef struct _virKeyFileParserCtxt virKeyFileParserCtxt;
|
||||
typedef virKeyFileParserCtxt *virKeyFileParserCtxtPtr;
|
||||
|
||||
struct _virKeyFile {
|
||||
virHashTablePtr groups;
|
||||
};
|
||||
|
||||
struct _virKeyFileParserCtxt {
|
||||
virKeyFilePtr conf;
|
||||
|
||||
const char *filename;
|
||||
|
||||
const char *base;
|
||||
const char *cur;
|
||||
const char *end;
|
||||
size_t line;
|
||||
|
||||
char *groupname;
|
||||
virHashTablePtr group;
|
||||
};
|
||||
|
||||
/*
|
||||
* The grammar for the keyfile
|
||||
*
|
||||
* KEYFILE = (GROUP | COMMENT | BLANK )*
|
||||
*
|
||||
* COMMENT = ('#' | ';') [^\n]* '\n'
|
||||
* BLANK = (' ' | '\t' )* '\n'
|
||||
*
|
||||
* GROUP = '[' GROUPNAME ']' '\n' (ENTRY ) *
|
||||
* GROUPNAME = [^[]\n]+
|
||||
*
|
||||
* ENTRY = KEYNAME '=' VALUE
|
||||
* VALUE = [^\n]* '\n'
|
||||
* KEYNAME = [-a-zA-Z0-9]+
|
||||
*/
|
||||
|
||||
#define IS_EOF (ctxt->cur >= ctxt->end)
|
||||
#define IS_EOL(c) (((c) == '\n') || ((c) == '\r'))
|
||||
#define IS_BLANK(c) (((c) == ' ') || ((c) == '\t'))
|
||||
#define IS_ASCII(c) (((unsigned char) (c)) < 128)
|
||||
#define CUR (*ctxt->cur)
|
||||
#define NEXT if (!IS_EOF) ctxt->cur++;
|
||||
|
||||
|
||||
#define virKeyFileError(ctxt, error, info) \
|
||||
virKeyFileErrorHelper(__FILE__, __FUNCTION__, __LINE__, ctxt, error, info)
|
||||
static void
|
||||
virKeyFileErrorHelper(const char *file, const char *func, size_t line,
|
||||
virKeyFileParserCtxtPtr ctxt,
|
||||
virErrorNumber error, const char *info)
|
||||
{
|
||||
/* Construct the string 'filename:line: info' if we have that. */
|
||||
if (ctxt && ctxt->filename) {
|
||||
virReportErrorHelper(VIR_FROM_CONF, error, file, func, line,
|
||||
_("%s:%zu: %s '%s'"), ctxt->filename, ctxt->line, info, ctxt->cur);
|
||||
} else {
|
||||
virReportErrorHelper(VIR_FROM_CONF, error, file, func, line,
|
||||
"%s", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int virKeyFileParseGroup(virKeyFileParserCtxtPtr ctxt)
|
||||
{
|
||||
int ret = -1;
|
||||
const char *name;
|
||||
NEXT;
|
||||
|
||||
ctxt->group = NULL;
|
||||
VIR_FREE(ctxt->groupname);
|
||||
|
||||
name = ctxt->cur;
|
||||
while (!IS_EOF && IS_ASCII(CUR) && CUR != ']')
|
||||
ctxt->cur++;
|
||||
if (CUR != ']') {
|
||||
virKeyFileError(ctxt, VIR_ERR_CONF_SYNTAX, "cannot find end of group name, expected ']'");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctxt->groupname = g_strndup(name, ctxt->cur - name);
|
||||
|
||||
NEXT;
|
||||
|
||||
if (!(ctxt->group = virHashCreate(10, virHashValueFree)))
|
||||
goto cleanup;
|
||||
|
||||
if (virHashAddEntry(ctxt->conf->groups, ctxt->groupname, ctxt->group) < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
if (ret != 0) {
|
||||
virHashFree(ctxt->group);
|
||||
ctxt->group = NULL;
|
||||
VIR_FREE(ctxt->groupname);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virKeyFileParseValue(virKeyFileParserCtxtPtr ctxt)
|
||||
{
|
||||
int ret = -1;
|
||||
const char *keystart;
|
||||
const char *valuestart;
|
||||
char *key = NULL;
|
||||
char *value = NULL;
|
||||
size_t len;
|
||||
|
||||
if (!ctxt->groupname || !ctxt->group) {
|
||||
virKeyFileError(ctxt, VIR_ERR_CONF_SYNTAX, "value found before first group");
|
||||
return -1;
|
||||
}
|
||||
|
||||
keystart = ctxt->cur;
|
||||
while (!IS_EOF && g_ascii_isalnum(CUR) && CUR != '=')
|
||||
ctxt->cur++;
|
||||
if (CUR != '=') {
|
||||
virKeyFileError(ctxt, VIR_ERR_CONF_SYNTAX, "expected end of value name, expected '='");
|
||||
return -1;
|
||||
}
|
||||
|
||||
key = g_strndup(keystart, ctxt->cur - keystart);
|
||||
|
||||
NEXT;
|
||||
valuestart = ctxt->cur;
|
||||
while (!IS_EOF && !IS_EOL(CUR))
|
||||
ctxt->cur++;
|
||||
if (!(IS_EOF || IS_EOL(CUR))) {
|
||||
virKeyFileError(ctxt, VIR_ERR_CONF_SYNTAX, "unexpected end of value");
|
||||
goto cleanup;
|
||||
}
|
||||
len = ctxt->cur - valuestart;
|
||||
if (IS_EOF && !IS_EOL(CUR))
|
||||
len++;
|
||||
value = g_strndup(valuestart, len);
|
||||
|
||||
if (virHashAddEntry(ctxt->group, key, value) < 0) {
|
||||
VIR_FREE(value);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
NEXT;
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virKeyFileParseComment(virKeyFileParserCtxtPtr ctxt)
|
||||
{
|
||||
NEXT;
|
||||
|
||||
while (!IS_EOF && !IS_EOL(CUR))
|
||||
ctxt->cur++;
|
||||
|
||||
NEXT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virKeyFileParseBlank(virKeyFileParserCtxtPtr ctxt)
|
||||
{
|
||||
while ((ctxt->cur < ctxt->end) && IS_BLANK(CUR))
|
||||
ctxt->cur++;
|
||||
|
||||
if (!((ctxt->cur == ctxt->end) || IS_EOL(CUR))) {
|
||||
virKeyFileError(ctxt, VIR_ERR_CONF_SYNTAX, "expected newline");
|
||||
return -1;
|
||||
}
|
||||
NEXT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virKeyFileParseStatement(virKeyFileParserCtxtPtr ctxt)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (CUR == '[') {
|
||||
ret = virKeyFileParseGroup(ctxt);
|
||||
} else if (g_ascii_isalnum(CUR)) {
|
||||
ret = virKeyFileParseValue(ctxt);
|
||||
} else if (CUR == '#' || CUR == ';') {
|
||||
ret = virKeyFileParseComment(ctxt);
|
||||
} else if (IS_BLANK(CUR) || IS_EOL(CUR)) {
|
||||
ret = virKeyFileParseBlank(ctxt);
|
||||
} else {
|
||||
virKeyFileError(ctxt, VIR_ERR_CONF_SYNTAX, "unexpected statement");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virKeyFileParse(virKeyFilePtr conf,
|
||||
const char *filename,
|
||||
const char *data,
|
||||
size_t len)
|
||||
{
|
||||
virKeyFileParserCtxt ctxt;
|
||||
int ret = -1;
|
||||
|
||||
VIR_DEBUG("Parse %p '%s' %p %zu", conf, filename, data, len);
|
||||
|
||||
memset(&ctxt, 0, sizeof(ctxt));
|
||||
|
||||
ctxt.filename = filename;
|
||||
ctxt.base = ctxt.cur = data;
|
||||
ctxt.end = data + len - 1;
|
||||
ctxt.line = 1;
|
||||
ctxt.conf = conf;
|
||||
|
||||
while (ctxt.cur < ctxt.end) {
|
||||
if (virKeyFileParseStatement(&ctxt) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
VIR_FREE(ctxt.groupname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void virKeyFileEntryFree(void *payload)
|
||||
{
|
||||
virHashFree(payload);
|
||||
}
|
||||
|
||||
|
||||
virKeyFilePtr virKeyFileNew(void)
|
||||
{
|
||||
virKeyFilePtr conf;
|
||||
|
||||
if (VIR_ALLOC(conf) < 0)
|
||||
goto error;
|
||||
|
||||
if (!(conf->groups = virHashCreate(10,
|
||||
virKeyFileEntryFree)))
|
||||
goto error;
|
||||
|
||||
return conf;
|
||||
|
||||
error:
|
||||
virKeyFileFree(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_CONFIG_FILE_SIZE (1024 * 1024)
|
||||
|
||||
int virKeyFileLoadFile(virKeyFilePtr conf,
|
||||
const char *filename)
|
||||
{
|
||||
char *data = NULL;
|
||||
ssize_t len;
|
||||
int ret;
|
||||
|
||||
if ((len = virFileReadAll(filename, MAX_CONFIG_FILE_SIZE, &data)) < 0)
|
||||
return -1;
|
||||
|
||||
ret = virKeyFileParse(conf, filename, data, len);
|
||||
|
||||
VIR_FREE(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int virKeyFileLoadData(virKeyFilePtr conf,
|
||||
const char *path,
|
||||
const char *data,
|
||||
size_t len)
|
||||
{
|
||||
return virKeyFileParse(conf, path, data, len);
|
||||
}
|
||||
|
||||
|
||||
void virKeyFileFree(virKeyFilePtr conf)
|
||||
{
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
virHashFree(conf->groups);
|
||||
VIR_FREE(conf);
|
||||
}
|
||||
|
||||
|
||||
bool virKeyFileHasGroup(virKeyFilePtr conf,
|
||||
const char *groupname)
|
||||
{
|
||||
VIR_DEBUG("conf=%p groupname=%s", conf, groupname);
|
||||
return virHashLookup(conf->groups, groupname) != NULL;
|
||||
}
|
||||
|
||||
|
||||
bool virKeyFileHasValue(virKeyFilePtr conf,
|
||||
const char *groupname,
|
||||
const char *valuename)
|
||||
{
|
||||
virHashTablePtr group = virHashLookup(conf->groups, groupname);
|
||||
VIR_DEBUG("conf=%p groupname=%s valuename=%s", conf, groupname, valuename);
|
||||
return group && virHashLookup(group, valuename) != NULL;
|
||||
}
|
||||
|
||||
const char *virKeyFileGetValueString(virKeyFilePtr conf,
|
||||
const char *groupname,
|
||||
const char *valuename)
|
||||
{
|
||||
virHashTablePtr group = virHashLookup(conf->groups, groupname);
|
||||
VIR_DEBUG("conf=%p groupname=%s valuename=%s", conf, groupname, valuename);
|
||||
return virHashLookup(group, valuename);
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* virkeyfile.h: "ini"-style configuration file handling
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/**
|
||||
* virKeyFilePtr:
|
||||
* a pointer to a parsed configuration file
|
||||
*/
|
||||
typedef struct _virKeyFile virKeyFile;
|
||||
typedef virKeyFile *virKeyFilePtr;
|
||||
|
||||
virKeyFilePtr virKeyFileNew(void);
|
||||
|
||||
int virKeyFileLoadFile(virKeyFilePtr conf,
|
||||
const char *filename)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
int virKeyFileLoadData(virKeyFilePtr conf,
|
||||
const char *filename,
|
||||
const char *data,
|
||||
size_t len)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
void virKeyFileFree(virKeyFilePtr conf);
|
||||
|
||||
bool virKeyFileHasGroup(virKeyFilePtr conf,
|
||||
const char *groupname)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
||||
|
||||
bool virKeyFileHasValue(virKeyFilePtr conf,
|
||||
const char *groupname,
|
||||
const char *valuename)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
|
||||
|
||||
const char *virKeyFileGetValueString(virKeyFilePtr conf,
|
||||
const char *groupname,
|
||||
const char *valuename)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
|
@ -176,7 +176,7 @@ test_programs = virshtest sockettest \
|
||||
commandtest seclabeltest \
|
||||
virhashtest virconftest \
|
||||
utiltest shunloadtest \
|
||||
virtimetest viruritest virkeyfiletest \
|
||||
virtimetest viruritest \
|
||||
viralloctest \
|
||||
virauthconfigtest \
|
||||
virbitmaptest \
|
||||
@ -1397,10 +1397,6 @@ viruritest_SOURCES = \
|
||||
viruritest.c testutils.h testutils.c
|
||||
viruritest_LDADD = $(LDADDS)
|
||||
|
||||
virkeyfiletest_SOURCES = \
|
||||
virkeyfiletest.c testutils.h testutils.c
|
||||
virkeyfiletest_LDADD = $(LDADDS)
|
||||
|
||||
viralloctest_SOURCES = \
|
||||
viralloctest.c testutils.h testutils.c
|
||||
viralloctest_LDADD = $(LDADDS)
|
||||
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011, 2012, 2014 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "testutils.h"
|
||||
#include "virerror.h"
|
||||
#include "viralloc.h"
|
||||
#include "virlog.h"
|
||||
|
||||
#include "virkeyfile.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_RPC
|
||||
|
||||
VIR_LOG_INIT("tests.keyfiletest");
|
||||
|
||||
static int testParse(const void *args G_GNUC_UNUSED)
|
||||
{
|
||||
static const char *cfg1 =
|
||||
"# Some config\n"
|
||||
"\n"
|
||||
"# The first group\n"
|
||||
"[Foo]\n"
|
||||
"one=The first entry is here\n"
|
||||
"two=The second entry\n"
|
||||
" \n"
|
||||
"three=The third entry\n"
|
||||
"[Bar]\n"
|
||||
"; Another comment\n"
|
||||
"one=The first entry in second group";
|
||||
virKeyFilePtr kf = virKeyFileNew();
|
||||
int ret = -1;
|
||||
|
||||
if (virKeyFileLoadData(kf, "demo.conf", cfg1, strlen(cfg1)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!virKeyFileHasGroup(kf, "Foo")) {
|
||||
VIR_DEBUG("Missing group 'Foo'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (!virKeyFileHasValue(kf, "Foo", "one")) {
|
||||
VIR_DEBUG("Missing Value 'Foo.one'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (!virKeyFileHasValue(kf, "Foo", "two")) {
|
||||
VIR_DEBUG("Missing Value 'Foo.two'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (!virKeyFileHasValue(kf, "Foo", "three")) {
|
||||
VIR_DEBUG("Missing Value 'Foo.three'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (STRNEQ(virKeyFileGetValueString(kf, "Foo", "one"),
|
||||
"The first entry is here")) {
|
||||
VIR_DEBUG("Wrong value for 'Foo.one'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (STRNEQ(virKeyFileGetValueString(kf, "Foo", "two"),
|
||||
"The second entry")) {
|
||||
VIR_DEBUG("Wrong value for 'Foo.one'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (STRNEQ(virKeyFileGetValueString(kf, "Foo", "three"),
|
||||
"The third entry")) {
|
||||
VIR_DEBUG("Wrong value for 'Foo.one'");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!virKeyFileHasGroup(kf, "Bar")) {
|
||||
VIR_DEBUG("Missing group 'Bar'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (!virKeyFileHasValue(kf, "Bar", "one")) {
|
||||
VIR_DEBUG("Missing Value 'Bar.one'");
|
||||
goto cleanup;
|
||||
}
|
||||
if (STRNEQ(virKeyFileGetValueString(kf, "Bar", "one"),
|
||||
"The first entry in second group")) {
|
||||
VIR_DEBUG("Wrong value for 'Bar.one'");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
virKeyFileFree(kf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mymain(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifndef WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif /* WIN32 */
|
||||
|
||||
if (virTestRun("Test parse", testParse, NULL) < 0)
|
||||
ret = -1;
|
||||
|
||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
VIR_TEST_MAIN(mymain)
|
Loading…
Reference in New Issue
Block a user