Commit ecec4fee authored by Chyla Zbigniew's avatar Chyla Zbigniew

Added FileProbeLevel argument. (GNUM_FILE_OPENER_CLASS,


* src/file.h
(typedef enum FileProbeLevel) New.
(typedef GnumFileOpenerProbeFunc, gnum_file_opener_probe): Added
FileProbeLevel argument.
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved to file-priv.h.

* src/file-priv.h
(struct _GnumFileOpenerClass::probe):
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved from file.h.

* src/file.c
(gnum_file_opener_probe_real, gnum_file_opener_probe):
Added FileProbeLevel argument.

* src/workbook-view.c
(wb_view_open_custom): Iterate over FileProbeLevel values when probing
the file (passing current value to probe function).

* src/xml-io.c
(xml_probe): Added FileProbeLevel argument, testing only file name if
probe level == FILE_PROBE_FILE_NAME.

* src/plugin-service.c (gnum_plugin_file_opener_probe):
Use file_patterns only if probe level == FILE_PROBE_FILE_NAME, otherwise
call module's probe function.

* src/bonobo-io.c (gnumeric_bonobo_io_probe),
src/plugin-service.h (struct _PluginServiceFileOpener),
src/plugin-loader-module.c
(gnumeric_plugin_loader_module_func_file_probe,
struct ServiceLoaderDataFileOpener::module_func_file_probe):
Added FileProbeLevel argument.
parent 42f47e02
2001-05-29 Zbigniew Chyla <cyba@gnome.pl>
* src/file.h
(typedef enum FileProbeLevel) New.
(typedef GnumFileOpenerProbeFunc, gnum_file_opener_probe): Added
FileProbeLevel argument.
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved to file-priv.h.
* src/file-priv.h
(struct _GnumFileOpenerClass::probe):
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved from file.h.
* src/file.c
(gnum_file_opener_probe_real, gnum_file_opener_probe):
Added FileProbeLevel argument.
* src/workbook-view.c
(wb_view_open_custom): Iterate over FileProbeLevel values when probing
the file (passing current value to probe function).
* src/xml-io.c
(xml_probe): Added FileProbeLevel argument, testing only file name if
probe level == FILE_PROBE_FILE_NAME.
* src/plugin-service.c (gnum_plugin_file_opener_probe):
Use file_patterns only if probe level == FILE_PROBE_FILE_NAME, otherwise
call module's probe function.
* src/bonobo-io.c (gnumeric_bonobo_io_probe),
src/plugin-service.h (struct _PluginServiceFileOpener),
src/plugin-loader-module.c
(gnumeric_plugin_loader_module_func_file_probe,
struct ServiceLoaderDataFileOpener::module_func_file_probe):
Added FileProbeLevel argument.
2001-05-27 Jody Goldberg <jgoldberg@home.com>
From Juan Pablo Mendoza <pablo_juan@yahoo.com>
......
2001-05-29 Zbigniew Chyla <cyba@gnome.pl>
* src/file.h
(typedef enum FileProbeLevel) New.
(typedef GnumFileOpenerProbeFunc, gnum_file_opener_probe): Added
FileProbeLevel argument.
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved to file-priv.h.
* src/file-priv.h
(struct _GnumFileOpenerClass::probe):
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved from file.h.
* src/file.c
(gnum_file_opener_probe_real, gnum_file_opener_probe):
Added FileProbeLevel argument.
* src/workbook-view.c
(wb_view_open_custom): Iterate over FileProbeLevel values when probing
the file (passing current value to probe function).
* src/xml-io.c
(xml_probe): Added FileProbeLevel argument, testing only file name if
probe level == FILE_PROBE_FILE_NAME.
* src/plugin-service.c (gnum_plugin_file_opener_probe):
Use file_patterns only if probe level == FILE_PROBE_FILE_NAME, otherwise
call module's probe function.
* src/bonobo-io.c (gnumeric_bonobo_io_probe),
src/plugin-service.h (struct _PluginServiceFileOpener),
src/plugin-loader-module.c
(gnumeric_plugin_loader_module_func_file_probe,
struct ServiceLoaderDataFileOpener::module_func_file_probe):
Added FileProbeLevel argument.
2001-05-27 Jody Goldberg <jgoldberg@home.com>
From Juan Pablo Mendoza <pablo_juan@yahoo.com>
......
2001-05-29 Zbigniew Chyla <cyba@gnome.pl>
* src/file.h
(typedef enum FileProbeLevel) New.
(typedef GnumFileOpenerProbeFunc, gnum_file_opener_probe): Added
FileProbeLevel argument.
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved to file-priv.h.
* src/file-priv.h
(struct _GnumFileOpenerClass::probe):
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved from file.h.
* src/file.c
(gnum_file_opener_probe_real, gnum_file_opener_probe):
Added FileProbeLevel argument.
* src/workbook-view.c
(wb_view_open_custom): Iterate over FileProbeLevel values when probing
the file (passing current value to probe function).
* src/xml-io.c
(xml_probe): Added FileProbeLevel argument, testing only file name if
probe level == FILE_PROBE_FILE_NAME.
* src/plugin-service.c (gnum_plugin_file_opener_probe):
Use file_patterns only if probe level == FILE_PROBE_FILE_NAME, otherwise
call module's probe function.
* src/bonobo-io.c (gnumeric_bonobo_io_probe),
src/plugin-service.h (struct _PluginServiceFileOpener),
src/plugin-loader-module.c
(gnumeric_plugin_loader_module_func_file_probe,
struct ServiceLoaderDataFileOpener::module_func_file_probe):
Added FileProbeLevel argument.
2001-05-27 Jody Goldberg <jgoldberg@home.com>
From Juan Pablo Mendoza <pablo_juan@yahoo.com>
......
2001-05-29 Zbigniew Chyla <cyba@gnome.pl>
* src/file.h
(typedef enum FileProbeLevel) New.
(typedef GnumFileOpenerProbeFunc, gnum_file_opener_probe): Added
FileProbeLevel argument.
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved to file-priv.h.
* src/file-priv.h
(struct _GnumFileOpenerClass::probe):
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved from file.h.
* src/file.c
(gnum_file_opener_probe_real, gnum_file_opener_probe):
Added FileProbeLevel argument.
* src/workbook-view.c
(wb_view_open_custom): Iterate over FileProbeLevel values when probing
the file (passing current value to probe function).
* src/xml-io.c
(xml_probe): Added FileProbeLevel argument, testing only file name if
probe level == FILE_PROBE_FILE_NAME.
* src/plugin-service.c (gnum_plugin_file_opener_probe):
Use file_patterns only if probe level == FILE_PROBE_FILE_NAME, otherwise
call module's probe function.
* src/bonobo-io.c (gnumeric_bonobo_io_probe),
src/plugin-service.h (struct _PluginServiceFileOpener),
src/plugin-loader-module.c
(gnumeric_plugin_loader_module_func_file_probe,
struct ServiceLoaderDataFileOpener::module_func_file_probe):
Added FileProbeLevel argument.
2001-05-27 Jody Goldberg <jgoldberg@home.com>
From Juan Pablo Mendoza <pablo_juan@yahoo.com>
......
2001-05-29 Zbigniew Chyla <cyba@gnome.pl>
* src/file.h
(typedef enum FileProbeLevel) New.
(typedef GnumFileOpenerProbeFunc, gnum_file_opener_probe): Added
FileProbeLevel argument.
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved to file-priv.h.
* src/file-priv.h
(struct _GnumFileOpenerClass::probe):
(GNUM_FILE_OPENER_CLASS, IS_GNUM_FILE_OPENER_CLASS,
GNUM_FILE_SAVER_CLASS, IS_GNUM_FILE_SAVER_CLASS):
Macros moved from file.h.
* src/file.c
(gnum_file_opener_probe_real, gnum_file_opener_probe):
Added FileProbeLevel argument.
* src/workbook-view.c
(wb_view_open_custom): Iterate over FileProbeLevel values when probing
the file (passing current value to probe function).
* src/xml-io.c
(xml_probe): Added FileProbeLevel argument, testing only file name if
probe level == FILE_PROBE_FILE_NAME.
* src/plugin-service.c (gnum_plugin_file_opener_probe):
Use file_patterns only if probe level == FILE_PROBE_FILE_NAME, otherwise
call module's probe function.
* src/bonobo-io.c (gnumeric_bonobo_io_probe),
src/plugin-service.h (struct _PluginServiceFileOpener),
src/plugin-loader-module.c
(gnumeric_plugin_loader_module_func_file_probe,
struct ServiceLoaderDataFileOpener::module_func_file_probe):
Added FileProbeLevel argument.
2001-05-27 Jody Goldberg <jgoldberg@home.com>
From Juan Pablo Mendoza <pablo_juan@yahoo.com>
......
......@@ -502,7 +502,8 @@ gnumeric_bonobo_read_workbook (GnumFileOpener const *fo,
}
static gboolean
gnumeric_bonobo_io_probe (GnumFileOpener const *fo, const char *filename)
gnumeric_bonobo_io_probe (GnumFileOpener const *fo, const char *filename,
FileProbeLevel pl)
{
char *p;
......
......@@ -4,6 +4,9 @@
/*
* GnumFileOpener
*/
#define GNUM_FILE_OPENER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_GNUM_FILE_OPENER, GnumFileOpenerClass))
#define IS_GNUM_FILE_OPENER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), TYPE_GNUM_FILE_OPENER))
#define GNUM_FILE_OPENER_METHOD(obj,name) \
((GNUM_FILE_OPENER_CLASS (GTK_OBJECT (obj)->klass))->name)
......@@ -12,7 +15,8 @@ struct _GnumFileOpenerClass {
GtkObjectClass parent_class;
gboolean (*probe) (GnumFileOpener const *fo,
const gchar *file_name);
const gchar *file_name,
FileProbeLevel pl);
void (*open) (GnumFileOpener const *fo,
IOContext *io_context,
WorkbookView *wbv,
......@@ -37,6 +41,9 @@ void gnum_file_opener_setup (GnumFileOpener *fo, const gchar *id,
* GnumFileSaver
*/
#define GNUM_FILE_SAVER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_GNUM_FILE_SAVER, GnumFileSaverClass))
#define IS_GNUM_FILE_SAVER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), TYPE_GNUM_FILE_SAVER))
#define GNUM_FILE_SAVER_METHOD(obj,name) \
((GNUM_FILE_SAVER_CLASS (GTK_OBJECT (obj)->klass))->name)
......
......@@ -49,13 +49,14 @@ gnum_file_opener_destroy (GtkObject *obj)
}
static gboolean
gnum_file_opener_probe_real (GnumFileOpener const *fo, const gchar *file_name)
gnum_file_opener_probe_real (GnumFileOpener const *fo, const gchar *file_name,
FileProbeLevel pl)
{
if (fo->probe_func == NULL) {
return FALSE;
}
return fo->probe_func (fo, file_name);
return fo->probe_func (fo, file_name, pl);
}
static void
......@@ -163,12 +164,12 @@ gnum_file_opener_get_description (GnumFileOpener const *fo)
* otherwise.
*/
gboolean
gnum_file_opener_probe (GnumFileOpener const *fo, const gchar *file_name)
gnum_file_opener_probe (GnumFileOpener const *fo, const gchar *file_name, FileProbeLevel pl)
{
g_return_val_if_fail (IS_GNUM_FILE_OPENER (fo), FALSE);
g_return_val_if_fail (file_name != NULL, FALSE);
return GNUM_FILE_OPENER_METHOD (fo, probe) (fo, file_name);
return GNUM_FILE_OPENER_METHOD (fo, probe) (fo, file_name, pl);
}
/**
......
......@@ -19,6 +19,23 @@ typedef enum {
FILE_FL_LAST
} FileFormatLevel;
/*
* File probe level tells file opener (its probe method to be exact), how
* hard it should try to recognize the type of the file. File openers may
* ignore this or support only some probe levels, but if specifies
* "reccomened" behaviour.
* Before opening any file we detect its type by calling probe for
* every registered file opener (in order of priority) and passing
* FILE_PROBE_FILE_NAME as probe level. If none of them recogizes the file,
* we increase probe level and try again...
*/
typedef enum {
FILE_PROBE_FILE_NAME, /* Test only file name, don't read file contents */
FILE_PROBE_CONTENT_FAST, /* Read only small parts of the file */
FILE_PROBE_CONTENT_FULL, /* Read the whole file if it's necessary */
FILE_PROBE_LAST
} FileProbeLevel;
/*
* GnumFileOpener
*/
......@@ -28,12 +45,11 @@ typedef struct _GnumFileOpenerClass GnumFileOpenerClass;
#define TYPE_GNUM_FILE_OPENER (gnum_file_opener_get_type ())
#define GNUM_FILE_OPENER(obj) (GTK_CHECK_CAST ((obj), TYPE_GNUM_FILE_OPENER, GnumFileOpener))
#define GNUM_FILE_OPENER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_GNUM_FILE_OPENER, GnumFileOpenerClass))
#define IS_GNUM_FILE_OPENER(obj) (GTK_CHECK_TYPE ((obj), TYPE_GNUM_FILE_OPENER))
#define IS_GNUM_FILE_OPENER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), TYPE_GNUM_FILE_OPENER))
typedef gboolean (*GnumFileOpenerProbeFunc) (GnumFileOpener const *fo,
const gchar *file_name);
const gchar *file_name,
FileProbeLevel pl);
typedef void (*GnumFileOpenerOpenFunc) (GnumFileOpener const *fo,
IOContext *io_context,
WorkbookView *wbv,
......@@ -46,7 +62,8 @@ GnumFileOpener *gnum_file_opener_new (const gchar *id,
GnumFileOpenerProbeFunc probe_func,
GnumFileOpenerOpenFunc open_func);
gboolean gnum_file_opener_probe (GnumFileOpener const *fo, const gchar *file_name);
gboolean gnum_file_opener_probe (GnumFileOpener const *fo, const gchar *file_name,
FileProbeLevel pl);
void gnum_file_opener_open (GnumFileOpener const *fo, IOContext *io_context,
WorkbookView *wbv, const gchar *file_name);
const gchar *gnum_file_opener_get_id (GnumFileOpener const *fo);
......@@ -61,9 +78,7 @@ typedef struct _GnumFileSaverClass GnumFileSaverClass;
#define TYPE_GNUM_FILE_SAVER (gnum_file_saver_get_type ())
#define GNUM_FILE_SAVER(obj) (GTK_CHECK_CAST ((obj), TYPE_GNUM_FILE_SAVER, GnumFileSaver))
#define GNUM_FILE_SAVER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), TYPE_GNUM_FILE_SAVER, GnumFileSaverClass))
#define IS_GNUM_FILE_SAVER(obj) (GTK_CHECK_TYPE ((obj), TYPE_GNUM_FILE_SAVER))
#define IS_GNUM_FILE_SAVER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), TYPE_GNUM_FILE_SAVER))
typedef void (*GnumFileSaverSaveFunc) (GnumFileSaver const *fs,
IOContext *io_context,
......
......@@ -13,6 +13,7 @@
#include <gnome-xml/parserInternals.h>
#include <gnome-xml/xmlmemory.h>
#include "gnumeric.h"
#include "file.h"
#include "plugin.h"
#include "plugin-service.h"
#include "plugin-loader.h"
......@@ -351,14 +352,15 @@ gnumeric_plugin_loader_module_load_service_general (GnumericPluginLoader *loader
*/
typedef struct {
gboolean (*module_func_file_probe) (GnumFileOpener const *fo, const gchar *file_name);
gboolean (*module_func_file_probe) (GnumFileOpener const *fo, const gchar *file_name,
FileProbeLevel pl);
void (*module_func_file_open) (GnumFileOpener const *fo, IOContext *io_context,
WorkbookView *wbv, const gchar *filename);
} ServiceLoaderDataFileOpener;
static gboolean
gnumeric_plugin_loader_module_func_file_probe (GnumFileOpener const *fo, PluginService *service,
const gchar *file_name)
const gchar *file_name, FileProbeLevel pl)
{
ServiceLoaderDataFileOpener *loader_data;
......@@ -366,7 +368,7 @@ gnumeric_plugin_loader_module_func_file_probe (GnumFileOpener const *fo, PluginS
g_return_val_if_fail (file_name != NULL, FALSE);
loader_data = (ServiceLoaderDataFileOpener *) plugin_service_get_loader_data (service);
return loader_data->module_func_file_probe (fo, file_name);
return loader_data->module_func_file_probe (fo, file_name, pl);
}
static void
......
......@@ -195,66 +195,62 @@ gnum_plugin_file_opener_init (GnumPluginFileOpener *fo)
}
static gboolean
gnum_plugin_file_opener_probe (GnumFileOpener const *fo, const gchar *file_name)
gnum_plugin_file_opener_probe (GnumFileOpener const *fo, const gchar *file_name,
FileProbeLevel pl)
{
GnumPluginFileOpener *pfo;
PluginServiceFileOpener *service_file_opener;
gboolean file_name_matches;
gchar *base_file_name = g_basename (file_name);
GList *l;
g_return_val_if_fail (IS_GNUM_PLUGIN_FILE_OPENER (fo), FALSE);
g_return_val_if_fail (file_name != NULL, FALSE);
pfo = GNUM_PLUGIN_FILE_OPENER (fo);
service_file_opener = &pfo->service->t.file_opener;
file_name_matches = FALSE;
for (l = service_file_opener->file_patterns; l != NULL; l = l->next) {
InputFilePattern *pattern;
pattern = (InputFilePattern *) l->data;
switch (pattern->pattern_type) {
case FILE_PATTERN_SHELL: {
if (pattern->case_sensitive) {
if (fnmatch (pattern->value, base_file_name, FNM_PATHNAME) == 0) {
file_name_matches = TRUE;
if (pl == FILE_PROBE_FILE_NAME && service_file_opener->file_patterns != NULL) {
gboolean match = FALSE;
GList *l;
for (l = service_file_opener->file_patterns; l != NULL && !match; l = l->next) {
InputFilePattern *pattern = l->data;
if (pattern->pattern_type == FILE_PATTERN_SHELL) {
if (pattern->case_sensitive) {
match = fnmatch (pattern->value, base_file_name, FNM_PATHNAME) == 0;
} else {
gchar *pattern_str, *name_str;
pattern_str = g_alloca (strlen (pattern->value) + 1);
name_str = g_alloca (strlen (base_file_name) + 1);
g_strdown (strcpy (pattern_str, pattern->value));
g_strdown (strcpy (name_str, base_file_name));
match = fnmatch (pattern_str, name_str, FNM_PATHNAME) == 0;
}
} else if (pattern->pattern_type == FILE_PATTERN_REGEXP) {
g_warning ("Not implemented");
} else {
gchar *pattern_str, *name_str;
pattern_str = g_alloca (strlen (pattern->value) + 1);
name_str = g_alloca (strlen (base_file_name) + 1);
g_strdown (strcpy (pattern_str, pattern->value));
g_strdown (strcpy (name_str, base_file_name));
if (fnmatch (pattern_str, name_str, FNM_PATHNAME) == 0) {
file_name_matches = TRUE;
}
}
break;
g_assert_not_reached ();
}
}
case FILE_PATTERN_REGEXP:
g_warning ("Not implemented");
break;
default:
g_assert_not_reached ();
}
return match;
}
if (service_file_opener->has_probe &&
(service_file_opener->file_patterns == NULL || file_name_matches)) {
if (service_file_opener->has_probe) {
ErrorInfo *ignored_error;
plugin_service_load (pfo->service, &ignored_error);
if (ignored_error == NULL) {
g_return_val_if_fail (service_file_opener->plugin_func_file_probe != NULL, FALSE);
return service_file_opener->plugin_func_file_probe (fo, pfo->service, file_name);
return service_file_opener->plugin_func_file_probe (fo, pfo->service, file_name, pl);
} else {
error_info_print (ignored_error);
error_info_free (ignored_error);
return FALSE;
}
} else {
return file_name_matches;
return FALSE;
}
}
......
......@@ -41,7 +41,7 @@ struct _PluginServiceFileOpener {
GnumFileOpener *opener;
/* fields available after loading */
gboolean (*plugin_func_file_probe) (GnumFileOpener const *fo, PluginService *service,
const gchar *file_name);
const gchar *file_name, FileProbeLevel pl);
void (*plugin_func_file_open) (GnumFileOpener const *fo, PluginService *service,
IOContext *io_context, WorkbookView *wbv,
const gchar *file_name);
......
......@@ -708,13 +708,16 @@ wb_view_open_custom (WorkbookView *wbv, WorkbookControl *wbc,
/* Search for an applicable opener */
if (fo == NULL) {
FileProbeLevel pl;
GList *l;
for (l = get_file_openers (); l != NULL; l = l->next) {
GnumFileOpener const *tmp_fo = GNUM_FILE_OPENER (l->data);
if (gnum_file_opener_probe (tmp_fo, file_name)) {
fo = tmp_fo;
break;
for (pl = FILE_PROBE_FILE_NAME; pl < FILE_PROBE_LAST && fo == NULL; pl++) {
for (l = get_file_openers (); l != NULL; l = l->next) {
GnumFileOpener const *tmp_fo = GNUM_FILE_OPENER (l->data);
if (gnum_file_opener_probe (tmp_fo, file_name, pl)) {
fo = tmp_fo;
break;
}
}
}
}
......
......@@ -3110,15 +3110,19 @@ xml_workbook_read (IOContext *context, WorkbookView *wb_view,
* passes, then we return TRUE
*/
static gboolean
xml_probe (GnumFileOpener const *fo, const gchar *filename)
xml_probe (GnumFileOpener const *fo, const gchar *filename, FileProbeLevel pl)
{
int ret;
int ret;
xmlDocPtr res = NULL;
xmlNsPtr gmr;
xmlParserCtxtPtr ctxt;
xmlSAXHandler silent, *old;
GnumericXMLVersion version;
if (pl == FILE_PROBE_FILE_NAME) {
return strcmp (g_extension_pointer (filename), "gnumeric") == 0;
}
/*
* Do a silent call to the XML parser.
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment